github.com/switchupcb/yaegi@v0.10.2/interp/interp.go (about)

     1  package interp
     2  
     3  import (
     4  	"bufio"
     5  	"context"
     6  	"errors"
     7  	"flag"
     8  	"fmt"
     9  	"go/build"
    10  	"go/constant"
    11  	"go/scanner"
    12  	"go/token"
    13  	"io"
    14  	"io/fs"
    15  	"log"
    16  	"math/bits"
    17  	"os"
    18  	"os/signal"
    19  	"path"
    20  	"path/filepath"
    21  	"reflect"
    22  	"strconv"
    23  	"strings"
    24  	"sync"
    25  	"sync/atomic"
    26  )
    27  
    28  // Interpreter node structure for AST and CFG.
    29  type node struct {
    30  	debug  *nodeDebugData // debug info
    31  	child  []*node        // child subtrees (AST)
    32  	anc    *node          // ancestor (AST)
    33  	start  *node          // entry point in subtree (CFG)
    34  	tnext  *node          // true branch successor (CFG)
    35  	fnext  *node          // false branch successor (CFG)
    36  	interp *Interpreter   // interpreter context
    37  	frame  *frame         // frame pointer used for closures only (TODO: suppress this)
    38  	index  int64          // node index (dot display)
    39  	findex int            // index of value in frame or frame size (func def, type def)
    40  	level  int            // number of frame indirections to access value
    41  	nleft  int            // number of children in left part (assign) or indicates preceding type (compositeLit)
    42  	nright int            // number of children in right part (assign)
    43  	kind   nkind          // kind of node
    44  	pos    token.Pos      // position in source code, relative to fset
    45  	sym    *symbol        // associated symbol
    46  	typ    *itype         // type of value in frame, or nil
    47  	recv   *receiver      // method receiver node for call, or nil
    48  	types  []reflect.Type // frame types, used by function literals only
    49  	scope  *scope         // frame scope
    50  	action action         // action
    51  	exec   bltn           // generated function to execute
    52  	gen    bltnGenerator  // generator function to produce above bltn
    53  	val    interface{}    // static generic value (CFG execution)
    54  	rval   reflect.Value  // reflection value to let runtime access interpreter (CFG)
    55  	ident  string         // set if node is a var or func
    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 aligment.
   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  	astDot bool // display AST graph (debug)
   173  	cfgDot bool // display CFG graph (debug)
   174  	// dotCmd is the command to process the dot graph produced when astDot and/or
   175  	// cfgDot is enabled. It defaults to 'dot -Tdot -o <filename>.dot'.
   176  	dotCmd       string
   177  	noRun        bool          // compile, but do not run
   178  	fastChan     bool          // disable cancellable chan operations
   179  	context      build.Context // build context: GOPATH, build constraints
   180  	specialStdio bool          // Allows os.Stdin, os.Stdout, os.Stderr to not be file descriptors
   181  	stdin        io.Reader     // standard input
   182  	stdout       io.Writer     // standard output
   183  	stderr       io.Writer     // standard error
   184  	args         []string      // cmdline args
   185  	filesystem   fs.FS
   186  	goCache      string // GOCACHE
   187  	goToolDir    string // GOTOOLDIR
   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  
   211  	mutex    sync.RWMutex
   212  	frame    *frame            // program data storage during execution
   213  	universe *scope            // interpreter global level scope
   214  	scopes   map[string]*scope // package level scopes, indexed by import path
   215  	srcPkg   imports           // source packages used in interpreter, indexed by path
   216  	pkgNames map[string]string // package names, indexed by import path
   217  	done     chan struct{}     // for cancellation of channel operations
   218  	roots    []*node
   219  
   220  	hooks *hooks // symbol hooks
   221  
   222  	debugger *Debugger
   223  }
   224  
   225  const (
   226  	mainID     = "main"
   227  	selfPrefix = "github.com/switchupcb/yaegi"
   228  	selfPath   = selfPrefix + "/interp/interp"
   229  	// DefaultSourceName is the name used by default when the name of the input
   230  	// source file has not been specified for an Eval.
   231  	// TODO(mpl): something even more special as a name?
   232  	DefaultSourceName = "_.go"
   233  
   234  	// Test is the value to pass to EvalPath to activate evaluation of test functions.
   235  	Test = false
   236  	// NoTest is the value to pass to EvalPath to skip evaluation of test functions.
   237  	NoTest = true
   238  )
   239  
   240  // Self points to the current interpreter if accessed from within itself, or is nil.
   241  var Self *Interpreter
   242  
   243  // Symbols exposes interpreter values.
   244  var Symbols = Exports{
   245  	selfPath: map[string]reflect.Value{
   246  		"New": reflect.ValueOf(New),
   247  
   248  		"Interpreter": reflect.ValueOf((*Interpreter)(nil)),
   249  		"Options":     reflect.ValueOf((*Options)(nil)),
   250  		"Panic":       reflect.ValueOf((*Panic)(nil)),
   251  	},
   252  }
   253  
   254  func init() { Symbols[selfPath]["Symbols"] = reflect.ValueOf(Symbols) }
   255  
   256  // _error is a wrapper of error interface type.
   257  type _error struct {
   258  	IValue interface{}
   259  	WError func() string
   260  }
   261  
   262  func (w _error) Error() string { return w.WError() }
   263  
   264  // Panic is an error recovered from a panic call in interpreted code.
   265  type Panic struct {
   266  	// Value is the recovered value of a call to panic.
   267  	Value interface{}
   268  
   269  	// Callers is the call stack obtained from the recover call.
   270  	// It may be used as the parameter to runtime.CallersFrames.
   271  	Callers []uintptr
   272  
   273  	// Stack is the call stack buffer for debug.
   274  	Stack []byte
   275  }
   276  
   277  // TODO: Capture interpreter stack frames also and remove
   278  // fmt.Fprintln(n.interp.stderr, oNode.cfgErrorf("panic")) in runCfg.
   279  
   280  func (e Panic) Error() string { return fmt.Sprint(e.Value) }
   281  
   282  // Walk traverses AST n in depth first order, call cbin function
   283  // at node entry and cbout function at node exit.
   284  func (n *node) Walk(in func(n *node) bool, out func(n *node)) {
   285  	if in != nil && !in(n) {
   286  		return
   287  	}
   288  	for _, child := range n.child {
   289  		child.Walk(in, out)
   290  	}
   291  	if out != nil {
   292  		out(n)
   293  	}
   294  }
   295  
   296  // Options are the interpreter options.
   297  type Options struct {
   298  	// GoPath sets GOPATH for the interpreter.
   299  	GoPath string
   300  
   301  	// GoCache sets GOCACHE for the interpreter.
   302  	GoCache string
   303  
   304  	// GoToolDir sets the GOTOOLDIR for the interpreter.
   305  	GoToolDir string
   306  
   307  	// BuildTags sets build constraints for the interpreter.
   308  	BuildTags []string
   309  
   310  	// Standard input, output and error streams.
   311  	// They default to os.Stdin, os.Stdout and os.Stderr respectively.
   312  	Stdin          io.Reader
   313  	Stdout, Stderr io.Writer
   314  
   315  	// Cmdline args, defaults to os.Args.
   316  	Args []string
   317  
   318  	// SourcecodeFilesystem is where the _sourcecode_ is loaded from and does
   319  	// NOT affect the filesystem of scripts when they run.
   320  	// It can be any fs.FS compliant filesystem (e.g. embed.FS, or fstest.MapFS for testing)
   321  	// See example/fs/fs_test.go for an example.
   322  	SourcecodeFilesystem fs.FS
   323  }
   324  
   325  // New returns a new interpreter.
   326  func New(options Options) *Interpreter {
   327  	i := Interpreter{
   328  		opt:      opt{context: build.Default, filesystem: &realFS{}, goCache: options.GoCache, goToolDir: options.GoToolDir},
   329  		frame:    newFrame(nil, 0, 0),
   330  		fset:     token.NewFileSet(),
   331  		universe: initUniverse(),
   332  		scopes:   map[string]*scope{},
   333  		binPkg:   Exports{"": map[string]reflect.Value{"_error": reflect.ValueOf((*_error)(nil))}},
   334  		srcPkg:   imports{},
   335  		pkgNames: map[string]string{},
   336  		rdir:     map[string]bool{},
   337  		hooks:    &hooks{},
   338  	}
   339  
   340  	if i.opt.stdin = options.Stdin; i.opt.stdin == nil {
   341  		i.opt.stdin = os.Stdin
   342  	}
   343  
   344  	if i.opt.stdout = options.Stdout; i.opt.stdout == nil {
   345  		i.opt.stdout = os.Stdout
   346  	}
   347  
   348  	if i.opt.stderr = options.Stderr; i.opt.stderr == nil {
   349  		i.opt.stderr = os.Stderr
   350  	}
   351  
   352  	if i.opt.args = options.Args; i.opt.args == nil {
   353  		i.opt.args = os.Args
   354  	}
   355  
   356  	if options.SourcecodeFilesystem != nil {
   357  		i.opt.filesystem = options.SourcecodeFilesystem
   358  	}
   359  
   360  	i.opt.context.GOPATH = options.GoPath
   361  	if len(options.BuildTags) > 0 {
   362  		i.opt.context.BuildTags = options.BuildTags
   363  	}
   364  
   365  	// astDot activates AST graph display for the interpreter
   366  	i.opt.astDot, _ = strconv.ParseBool(os.Getenv("YAEGI_AST_DOT"))
   367  
   368  	// cfgDot activates CFG graph display for the interpreter
   369  	i.opt.cfgDot, _ = strconv.ParseBool(os.Getenv("YAEGI_CFG_DOT"))
   370  
   371  	// dotCmd defines how to process the dot code generated whenever astDot and/or
   372  	// cfgDot is enabled. It defaults to 'dot -Tdot -o<filename>.dot' where filename
   373  	// is context dependent.
   374  	i.opt.dotCmd = os.Getenv("YAEGI_DOT_CMD")
   375  
   376  	// noRun disables the execution (but not the compilation) in the interpreter
   377  	i.opt.noRun, _ = strconv.ParseBool(os.Getenv("YAEGI_NO_RUN"))
   378  
   379  	// fastChan disables the cancellable version of channel operations in evalWithContext
   380  	i.opt.fastChan, _ = strconv.ParseBool(os.Getenv("YAEGI_FAST_CHAN"))
   381  
   382  	// specialStdio allows to assign directly io.Writer and io.Reader to os.Stdxxx,
   383  	// even if they are not file descriptors.
   384  	i.opt.specialStdio, _ = strconv.ParseBool(os.Getenv("YAEGI_SPECIAL_STDIO"))
   385  
   386  	return &i
   387  }
   388  
   389  const (
   390  	bltnAppend  = "append"
   391  	bltnCap     = "cap"
   392  	bltnClose   = "close"
   393  	bltnComplex = "complex"
   394  	bltnImag    = "imag"
   395  	bltnCopy    = "copy"
   396  	bltnDelete  = "delete"
   397  	bltnLen     = "len"
   398  	bltnMake    = "make"
   399  	bltnNew     = "new"
   400  	bltnPanic   = "panic"
   401  	bltnPrint   = "print"
   402  	bltnPrintln = "println"
   403  	bltnReal    = "real"
   404  	bltnRecover = "recover"
   405  )
   406  
   407  func initUniverse() *scope {
   408  	sc := &scope{global: true, sym: map[string]*symbol{
   409  		// predefined Go types
   410  		"bool":        {kind: typeSym, typ: &itype{cat: boolT, name: "bool", str: "bool"}},
   411  		"byte":        {kind: typeSym, typ: &itype{cat: uint8T, name: "uint8", str: "uint8"}},
   412  		"complex64":   {kind: typeSym, typ: &itype{cat: complex64T, name: "complex64", str: "complex64"}},
   413  		"complex128":  {kind: typeSym, typ: &itype{cat: complex128T, name: "complex128", str: "complex128"}},
   414  		"error":       {kind: typeSym, typ: &itype{cat: errorT, name: "error", str: "error"}},
   415  		"float32":     {kind: typeSym, typ: &itype{cat: float32T, name: "float32", str: "float32"}},
   416  		"float64":     {kind: typeSym, typ: &itype{cat: float64T, name: "float64", str: "float64"}},
   417  		"int":         {kind: typeSym, typ: &itype{cat: intT, name: "int", str: "int"}},
   418  		"int8":        {kind: typeSym, typ: &itype{cat: int8T, name: "int8", str: "int8"}},
   419  		"int16":       {kind: typeSym, typ: &itype{cat: int16T, name: "int16", str: "int16"}},
   420  		"int32":       {kind: typeSym, typ: &itype{cat: int32T, name: "int32", str: "int32"}},
   421  		"int64":       {kind: typeSym, typ: &itype{cat: int64T, name: "int64", str: "int64"}},
   422  		"interface{}": {kind: typeSym, typ: &itype{cat: interfaceT, str: "interface{}"}},
   423  		"rune":        {kind: typeSym, typ: &itype{cat: int32T, name: "int32", str: "int32"}},
   424  		"string":      {kind: typeSym, typ: &itype{cat: stringT, name: "string", str: "string"}},
   425  		"uint":        {kind: typeSym, typ: &itype{cat: uintT, name: "uint", str: "uint"}},
   426  		"uint8":       {kind: typeSym, typ: &itype{cat: uint8T, name: "uint8", str: "uint8"}},
   427  		"uint16":      {kind: typeSym, typ: &itype{cat: uint16T, name: "uint16", str: "uint16"}},
   428  		"uint32":      {kind: typeSym, typ: &itype{cat: uint32T, name: "uint32", str: "uint32"}},
   429  		"uint64":      {kind: typeSym, typ: &itype{cat: uint64T, name: "uint64", str: "uint64"}},
   430  		"uintptr":     {kind: typeSym, typ: &itype{cat: uintptrT, name: "uintptr", str: "uintptr"}},
   431  
   432  		// predefined Go constants
   433  		"false": {kind: constSym, typ: untypedBool(), rval: reflect.ValueOf(false)},
   434  		"true":  {kind: constSym, typ: untypedBool(), rval: reflect.ValueOf(true)},
   435  		"iota":  {kind: constSym, typ: untypedInt()},
   436  
   437  		// predefined Go zero value
   438  		"nil": {typ: &itype{cat: nilT, untyped: true, str: "nil"}},
   439  
   440  		// predefined Go builtins
   441  		bltnAppend:  {kind: bltnSym, builtin: _append},
   442  		bltnCap:     {kind: bltnSym, builtin: _cap},
   443  		bltnClose:   {kind: bltnSym, builtin: _close},
   444  		bltnComplex: {kind: bltnSym, builtin: _complex},
   445  		bltnImag:    {kind: bltnSym, builtin: _imag},
   446  		bltnCopy:    {kind: bltnSym, builtin: _copy},
   447  		bltnDelete:  {kind: bltnSym, builtin: _delete},
   448  		bltnLen:     {kind: bltnSym, builtin: _len},
   449  		bltnMake:    {kind: bltnSym, builtin: _make},
   450  		bltnNew:     {kind: bltnSym, builtin: _new},
   451  		bltnPanic:   {kind: bltnSym, builtin: _panic},
   452  		bltnPrint:   {kind: bltnSym, builtin: _print},
   453  		bltnPrintln: {kind: bltnSym, builtin: _println},
   454  		bltnReal:    {kind: bltnSym, builtin: _real},
   455  		bltnRecover: {kind: bltnSym, builtin: _recover},
   456  	}}
   457  	return sc
   458  }
   459  
   460  // resizeFrame resizes the global frame of interpreter.
   461  func (interp *Interpreter) resizeFrame() {
   462  	l := len(interp.universe.types)
   463  	b := len(interp.frame.data)
   464  	if l-b <= 0 {
   465  		return
   466  	}
   467  	data := make([]reflect.Value, l)
   468  	copy(data, interp.frame.data)
   469  	for j, t := range interp.universe.types[b:] {
   470  		data[b+j] = reflect.New(t).Elem()
   471  	}
   472  	interp.frame.data = data
   473  }
   474  
   475  // Eval evaluates Go code represented as a string. Eval returns the last result
   476  // computed by the interpreter, and a non nil error in case of failure.
   477  func (interp *Interpreter) Eval(src string) (res reflect.Value, err error) {
   478  	return interp.eval(src, "", true)
   479  }
   480  
   481  // EvalPath evaluates Go code located at path and returns the last result computed
   482  // by the interpreter, and a non nil error in case of failure.
   483  // The main function of the main package is executed if present.
   484  func (interp *Interpreter) EvalPath(path string) (res reflect.Value, err error) {
   485  	if !isFile(interp.opt.filesystem, path) {
   486  		_, err := interp.importSrc(mainID, path, NoTest)
   487  		return res, err
   488  	}
   489  
   490  	b, err := fs.ReadFile(interp.filesystem, path)
   491  	if err != nil {
   492  		return res, err
   493  	}
   494  	return interp.eval(string(b), path, false)
   495  }
   496  
   497  // EvalPathWithContext evaluates Go code located at path and returns the last
   498  // result computed by the interpreter, and a non nil error in case of failure.
   499  // The main function of the main package is executed if present.
   500  func (interp *Interpreter) EvalPathWithContext(ctx context.Context, path string) (res reflect.Value, err error) {
   501  	interp.mutex.Lock()
   502  	interp.done = make(chan struct{})
   503  	interp.cancelChan = !interp.opt.fastChan
   504  	interp.mutex.Unlock()
   505  
   506  	done := make(chan struct{})
   507  	go func() {
   508  		defer close(done)
   509  		res, err = interp.EvalPath(path)
   510  	}()
   511  
   512  	select {
   513  	case <-ctx.Done():
   514  		interp.stop()
   515  		return reflect.Value{}, ctx.Err()
   516  	case <-done:
   517  	}
   518  	return res, err
   519  }
   520  
   521  // EvalTest evaluates Go code located at path, including test files with "_test.go" suffix.
   522  // A non nil error is returned in case of failure.
   523  // The main function, test functions and benchmark functions are internally compiled but not
   524  // executed. Test functions can be retrieved using the Symbol() method.
   525  func (interp *Interpreter) EvalTest(path string) error {
   526  	_, err := interp.importSrc(mainID, path, Test)
   527  	return err
   528  }
   529  
   530  // Symbols returns a map of interpreter exported symbol values for the given
   531  // import path. If the argument is the empty string, all known symbols are
   532  // returned.
   533  func (interp *Interpreter) Symbols(importPath string) Exports {
   534  	m := map[string]map[string]reflect.Value{}
   535  	interp.mutex.RLock()
   536  	defer interp.mutex.RUnlock()
   537  
   538  	for k, v := range interp.srcPkg {
   539  		if importPath != "" && k != importPath {
   540  			continue
   541  		}
   542  		syms := map[string]reflect.Value{}
   543  		for n, s := range v {
   544  			if !canExport(n) {
   545  				// Skip private non-exported symbols.
   546  				continue
   547  			}
   548  			switch s.kind {
   549  			case constSym:
   550  				syms[n] = s.rval
   551  			case funcSym:
   552  				syms[n] = genFunctionWrapper(s.node)(interp.frame)
   553  			case varSym:
   554  				syms[n] = interp.frame.data[s.index]
   555  			case typeSym:
   556  				syms[n] = reflect.New(s.typ.TypeOf())
   557  			}
   558  		}
   559  
   560  		if len(syms) > 0 {
   561  			m[k] = syms
   562  		}
   563  
   564  		if importPath != "" {
   565  			return m
   566  		}
   567  	}
   568  
   569  	if importPath != "" && len(m) > 0 {
   570  		return m
   571  	}
   572  
   573  	for k, v := range interp.binPkg {
   574  		if importPath != "" && k != importPath {
   575  			continue
   576  		}
   577  		m[k] = v
   578  		if importPath != "" {
   579  			return m
   580  		}
   581  	}
   582  
   583  	return m
   584  }
   585  
   586  func isFile(filesystem fs.FS, path string) bool {
   587  	fi, err := fs.Stat(filesystem, path)
   588  	return err == nil && fi.Mode().IsRegular()
   589  }
   590  
   591  func (interp *Interpreter) eval(src, name string, inc bool) (res reflect.Value, err error) {
   592  	prog, err := interp.compileSrc(src, name, inc)
   593  	if err != nil {
   594  		return res, err
   595  	}
   596  
   597  	if interp.noRun {
   598  		return res, err
   599  	}
   600  
   601  	return interp.Execute(prog)
   602  }
   603  
   604  // EvalWithContext evaluates Go code represented as a string. It returns
   605  // a map on current interpreted package exported symbols.
   606  func (interp *Interpreter) EvalWithContext(ctx context.Context, src string) (reflect.Value, error) {
   607  	var v reflect.Value
   608  	var err error
   609  
   610  	interp.mutex.Lock()
   611  	interp.done = make(chan struct{})
   612  	interp.cancelChan = !interp.opt.fastChan
   613  	interp.mutex.Unlock()
   614  
   615  	done := make(chan struct{})
   616  	go func() {
   617  		defer close(done)
   618  		v, err = interp.Eval(src)
   619  	}()
   620  
   621  	select {
   622  	case <-ctx.Done():
   623  		interp.stop()
   624  		return reflect.Value{}, ctx.Err()
   625  	case <-done:
   626  	}
   627  	return v, err
   628  }
   629  
   630  // stop sends a semaphore to all running frames and closes the chan
   631  // operation short circuit channel. stop may only be called once per
   632  // invocation of EvalWithContext.
   633  func (interp *Interpreter) stop() {
   634  	atomic.AddUint64(&interp.id, 1)
   635  	close(interp.done)
   636  }
   637  
   638  func (interp *Interpreter) runid() uint64 { return atomic.LoadUint64(&interp.id) }
   639  
   640  // getWrapper returns the wrapper type of the corresponding interface, or nil if not found.
   641  func (interp *Interpreter) getWrapper(t reflect.Type) reflect.Type {
   642  	if p, ok := interp.binPkg[t.PkgPath()]; ok {
   643  		return p["_"+t.Name()].Type().Elem()
   644  	}
   645  	return nil
   646  }
   647  
   648  // Use loads binary runtime symbols in the interpreter context so
   649  // they can be used in interpreted code.
   650  func (interp *Interpreter) Use(values Exports) error {
   651  	for k, v := range values {
   652  		importPath := path.Dir(k)
   653  		packageName := path.Base(k)
   654  
   655  		if importPath == "." {
   656  			return fmt.Errorf("export path %[1]q is missing a package name; did you mean '%[1]s/%[1]s'?", k)
   657  		}
   658  
   659  		if importPath == selfPrefix {
   660  			interp.hooks.Parse(v)
   661  			continue
   662  		}
   663  
   664  		if interp.binPkg[importPath] == nil {
   665  			interp.binPkg[importPath] = make(map[string]reflect.Value)
   666  			interp.pkgNames[importPath] = packageName
   667  		}
   668  
   669  		for s, sym := range v {
   670  			interp.binPkg[importPath][s] = sym
   671  		}
   672  		if k == selfPath {
   673  			interp.binPkg[importPath]["Self"] = reflect.ValueOf(interp)
   674  		}
   675  	}
   676  
   677  	// Checks if input values correspond to stdlib packages by looking for one
   678  	// well known stdlib package path.
   679  	if _, ok := values["fmt/fmt"]; ok {
   680  		fixStdlib(interp)
   681  	}
   682  	return nil
   683  }
   684  
   685  // fixStdlib redefines interpreter stdlib symbols to use the standard input,
   686  // output and errror assigned to the interpreter. The changes are limited to
   687  // the interpreter only.
   688  // Note that it is possible to escape the virtualized stdio by
   689  // read/write directly to file descriptors 0, 1, 2.
   690  func fixStdlib(interp *Interpreter) {
   691  	p := interp.binPkg["fmt"]
   692  	if p == nil {
   693  		return
   694  	}
   695  
   696  	stdin, stdout, stderr := interp.stdin, interp.stdout, interp.stderr
   697  
   698  	p["Print"] = reflect.ValueOf(func(a ...interface{}) (n int, err error) { return fmt.Fprint(stdout, a...) })
   699  	p["Printf"] = reflect.ValueOf(func(f string, a ...interface{}) (n int, err error) { return fmt.Fprintf(stdout, f, a...) })
   700  	p["Println"] = reflect.ValueOf(func(a ...interface{}) (n int, err error) { return fmt.Fprintln(stdout, a...) })
   701  
   702  	p["Scan"] = reflect.ValueOf(func(a ...interface{}) (n int, err error) { return fmt.Fscan(stdin, a...) })
   703  	p["Scanf"] = reflect.ValueOf(func(f string, a ...interface{}) (n int, err error) { return fmt.Fscanf(stdin, f, a...) })
   704  	p["Scanln"] = reflect.ValueOf(func(a ...interface{}) (n int, err error) { return fmt.Fscanln(stdin, a...) })
   705  
   706  	if p = interp.binPkg["flag"]; p != nil {
   707  		c := flag.NewFlagSet(os.Args[0], flag.PanicOnError)
   708  		c.SetOutput(stderr)
   709  		p["CommandLine"] = reflect.ValueOf(&c).Elem()
   710  	}
   711  
   712  	if p = interp.binPkg["log"]; p != nil {
   713  		l := log.New(stderr, "", log.LstdFlags)
   714  		// Restrict Fatal symbols to panic instead of exit.
   715  		p["Fatal"] = reflect.ValueOf(l.Panic)
   716  		p["Fatalf"] = reflect.ValueOf(l.Panicf)
   717  		p["Fatalln"] = reflect.ValueOf(l.Panicln)
   718  
   719  		p["Flags"] = reflect.ValueOf(l.Flags)
   720  		p["Output"] = reflect.ValueOf(l.Output)
   721  		p["Panic"] = reflect.ValueOf(l.Panic)
   722  		p["Panicf"] = reflect.ValueOf(l.Panicf)
   723  		p["Panicln"] = reflect.ValueOf(l.Panicln)
   724  		p["Prefix"] = reflect.ValueOf(l.Prefix)
   725  		p["Print"] = reflect.ValueOf(l.Print)
   726  		p["Printf"] = reflect.ValueOf(l.Printf)
   727  		p["Println"] = reflect.ValueOf(l.Println)
   728  		p["SetFlags"] = reflect.ValueOf(l.SetFlags)
   729  		p["SetOutput"] = reflect.ValueOf(l.SetOutput)
   730  		p["SetPrefix"] = reflect.ValueOf(l.SetPrefix)
   731  		p["Writer"] = reflect.ValueOf(l.Writer)
   732  	}
   733  
   734  	if p = interp.binPkg["os"]; p != nil {
   735  		p["Args"] = reflect.ValueOf(&interp.args).Elem()
   736  		if interp.specialStdio {
   737  			// Inherit streams from interpreter even if they do not have a file descriptor.
   738  			p["Stdin"] = reflect.ValueOf(&stdin).Elem()
   739  			p["Stdout"] = reflect.ValueOf(&stdout).Elem()
   740  			p["Stderr"] = reflect.ValueOf(&stderr).Elem()
   741  		} else {
   742  			// Inherits streams from interpreter only if they have a file descriptor and preserve original type.
   743  			if s, ok := stdin.(*os.File); ok {
   744  				p["Stdin"] = reflect.ValueOf(&s).Elem()
   745  			}
   746  			if s, ok := stdout.(*os.File); ok {
   747  				p["Stdout"] = reflect.ValueOf(&s).Elem()
   748  			}
   749  			if s, ok := stderr.(*os.File); ok {
   750  				p["Stderr"] = reflect.ValueOf(&s).Elem()
   751  			}
   752  		}
   753  	}
   754  
   755  	if p = interp.binPkg["math/bits"]; p != nil {
   756  		// Do not trust extracted value maybe from another arch.
   757  		p["UintSize"] = reflect.ValueOf(constant.MakeInt64(bits.UintSize))
   758  	}
   759  }
   760  
   761  // ignoreScannerError returns true if the error from Go scanner can be safely ignored
   762  // to let the caller grab one more line before retrying to parse its input.
   763  func ignoreScannerError(e *scanner.Error, s string) bool {
   764  	msg := e.Msg
   765  	if strings.HasSuffix(msg, "found 'EOF'") {
   766  		return true
   767  	}
   768  	if msg == "raw string literal not terminated" {
   769  		return true
   770  	}
   771  	if strings.HasPrefix(msg, "expected operand, found '}'") && !strings.HasSuffix(s, "}") {
   772  		return true
   773  	}
   774  	return false
   775  }
   776  
   777  // ImportUsed automatically imports pre-compiled packages included by Use().
   778  // This is mainly useful for REPLs, or single command lines. In case of an ambiguous default
   779  // package name, for example "rand" for crypto/rand and math/rand, the package name is
   780  // constructed by replacing the last "/" by a "_", producing crypto_rand and math_rand.
   781  // ImportUsed should not be called more than once, and not after a first Eval, as it may
   782  // rename packages.
   783  func (interp *Interpreter) ImportUsed() {
   784  	sc := interp.universe
   785  	for k := range interp.binPkg {
   786  		// By construction, the package name is the last path element of the key.
   787  		name := path.Base(k)
   788  		if sym, ok := sc.sym[name]; ok {
   789  			// Handle collision by renaming old and new entries.
   790  			name2 := key2name(fixKey(sym.typ.path))
   791  			sc.sym[name2] = sym
   792  			if name2 != name {
   793  				delete(sc.sym, name)
   794  			}
   795  			name = key2name(fixKey(k))
   796  		}
   797  		sc.sym[name] = &symbol{kind: pkgSym, typ: &itype{cat: binPkgT, path: k, scope: sc}}
   798  	}
   799  }
   800  
   801  func key2name(name string) string {
   802  	return filepath.Join(name, DefaultSourceName)
   803  }
   804  
   805  func fixKey(k string) string {
   806  	i := strings.LastIndex(k, "/")
   807  	if i >= 0 {
   808  		k = k[:i] + "_" + k[i+1:]
   809  	}
   810  	return k
   811  }
   812  
   813  // REPL performs a Read-Eval-Print-Loop on input reader.
   814  // Results are printed to the output writer of the Interpreter, provided as option
   815  // at creation time. Errors are printed to the similarly defined errors writer.
   816  // The last interpreter result value and error are returned.
   817  func (interp *Interpreter) REPL() (reflect.Value, error) {
   818  	in, out, errs := interp.stdin, interp.stdout, interp.stderr
   819  	ctx, cancel := context.WithCancel(context.Background())
   820  	end := make(chan struct{})     // channel to terminate the REPL
   821  	sig := make(chan os.Signal, 1) // channel to trap interrupt signal (Ctrl-C)
   822  	lines := make(chan string)     // channel to read REPL input lines
   823  	prompt := getPrompt(in, out)   // prompt activated on tty like IO stream
   824  	s := bufio.NewScanner(in)      // read input stream line by line
   825  	var v reflect.Value            // result value from eval
   826  	var err error                  // error from eval
   827  	src := ""                      // source string to evaluate
   828  
   829  	signal.Notify(sig, os.Interrupt)
   830  	defer signal.Stop(sig)
   831  	prompt(v)
   832  
   833  	go func() {
   834  		defer close(end)
   835  		for s.Scan() {
   836  			lines <- s.Text()
   837  		}
   838  		if e := s.Err(); e != nil {
   839  			fmt.Fprintln(errs, e)
   840  		}
   841  	}()
   842  
   843  	go func() {
   844  		for {
   845  			select {
   846  			case <-sig:
   847  				cancel()
   848  				lines <- ""
   849  			case <-end:
   850  				return
   851  			}
   852  		}
   853  	}()
   854  
   855  	for {
   856  		var line string
   857  
   858  		select {
   859  		case <-end:
   860  			cancel()
   861  			return v, err
   862  		case line = <-lines:
   863  			src += line + "\n"
   864  		}
   865  
   866  		v, err = interp.EvalWithContext(ctx, src)
   867  		if err != nil {
   868  			switch e := err.(type) {
   869  			case scanner.ErrorList:
   870  				if len(e) > 0 && ignoreScannerError(e[0], line) {
   871  					continue
   872  				}
   873  				fmt.Fprintln(errs, strings.TrimPrefix(e[0].Error(), DefaultSourceName+":"))
   874  			case Panic:
   875  				fmt.Fprintln(errs, e.Value)
   876  				fmt.Fprintln(errs, string(e.Stack))
   877  			default:
   878  				fmt.Fprintln(errs, err)
   879  			}
   880  		}
   881  		if errors.Is(err, context.Canceled) {
   882  			ctx, cancel = context.WithCancel(context.Background())
   883  		}
   884  		src = ""
   885  		prompt(v)
   886  	}
   887  }
   888  
   889  func doPrompt(out io.Writer) func(v reflect.Value) {
   890  	return func(v reflect.Value) {
   891  		if v.IsValid() {
   892  			fmt.Fprintln(out, ":", v)
   893  		}
   894  		fmt.Fprint(out, "> ")
   895  	}
   896  }
   897  
   898  // getPrompt returns a function which prints a prompt only if input is a terminal.
   899  func getPrompt(in io.Reader, out io.Writer) func(reflect.Value) {
   900  	forcePrompt, _ := strconv.ParseBool(os.Getenv("YAEGI_PROMPT"))
   901  	if forcePrompt {
   902  		return doPrompt(out)
   903  	}
   904  	s, ok := in.(interface{ Stat() (os.FileInfo, error) })
   905  	if !ok {
   906  		return func(reflect.Value) {}
   907  	}
   908  	stat, err := s.Stat()
   909  	if err == nil && stat.Mode()&os.ModeCharDevice != 0 {
   910  		return doPrompt(out)
   911  	}
   912  	return func(reflect.Value) {}
   913  }