gitlab.com/ethan.reesor/vscode-notebooks/yaegi@v0.0.0-20220417214422-5c573557938e/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 // 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 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 = "gitlab.com/ethan.reesor/vscode-notebooks/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 // BuildTags sets build constraints for the interpreter. 302 BuildTags []string 303 304 // Standard input, output and error streams. 305 // They default to os.Stdin, os.Stdout and os.Stderr respectively. 306 Stdin io.Reader 307 Stdout, Stderr io.Writer 308 309 // Cmdline args, defaults to os.Args. 310 Args []string 311 312 // Environment of interpreter. Entries are in the form "key=values". 313 Env []string 314 315 // SourcecodeFilesystem is where the _sourcecode_ is loaded from and does 316 // NOT affect the filesystem of scripts when they run. 317 // It can be any fs.FS compliant filesystem (e.g. embed.FS, or fstest.MapFS for testing) 318 // See example/fs/fs_test.go for an example. 319 SourcecodeFilesystem fs.FS 320 321 // Unrestricted allows to run non sandboxed stdlib symbols such as os/exec and environment 322 Unrestricted bool 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{}, env: map[string]string{}}, 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 // unrestricted allows to use non sandboxed stdlib symbols and env. 357 if options.Unrestricted { 358 i.opt.unrestricted = true 359 } else { 360 for _, e := range options.Env { 361 a := strings.SplitN(e, "=", 2) 362 if len(a) == 2 { 363 i.opt.env[a[0]] = a[1] 364 } else { 365 i.opt.env[a[0]] = "" 366 } 367 } 368 } 369 370 if options.SourcecodeFilesystem != nil { 371 i.opt.filesystem = options.SourcecodeFilesystem 372 } 373 374 i.opt.context.GOPATH = options.GoPath 375 if len(options.BuildTags) > 0 { 376 i.opt.context.BuildTags = options.BuildTags 377 } 378 379 // astDot activates AST graph display for the interpreter 380 i.opt.astDot, _ = strconv.ParseBool(os.Getenv("YAEGI_AST_DOT")) 381 382 // cfgDot activates CFG graph display for the interpreter 383 i.opt.cfgDot, _ = strconv.ParseBool(os.Getenv("YAEGI_CFG_DOT")) 384 385 // dotCmd defines how to process the dot code generated whenever astDot and/or 386 // cfgDot is enabled. It defaults to 'dot -Tdot -o<filename>.dot' where filename 387 // is context dependent. 388 i.opt.dotCmd = os.Getenv("YAEGI_DOT_CMD") 389 390 // noRun disables the execution (but not the compilation) in the interpreter 391 i.opt.noRun, _ = strconv.ParseBool(os.Getenv("YAEGI_NO_RUN")) 392 393 // fastChan disables the cancellable version of channel operations in evalWithContext 394 i.opt.fastChan, _ = strconv.ParseBool(os.Getenv("YAEGI_FAST_CHAN")) 395 396 // specialStdio allows to assign directly io.Writer and io.Reader to os.Stdxxx, 397 // even if they are not file descriptors. 398 i.opt.specialStdio, _ = strconv.ParseBool(os.Getenv("YAEGI_SPECIAL_STDIO")) 399 400 return &i 401 } 402 403 const ( 404 bltnAppend = "append" 405 bltnCap = "cap" 406 bltnClose = "close" 407 bltnComplex = "complex" 408 bltnImag = "imag" 409 bltnCopy = "copy" 410 bltnDelete = "delete" 411 bltnLen = "len" 412 bltnMake = "make" 413 bltnNew = "new" 414 bltnPanic = "panic" 415 bltnPrint = "print" 416 bltnPrintln = "println" 417 bltnReal = "real" 418 bltnRecover = "recover" 419 ) 420 421 func initUniverse() *scope { 422 sc := &scope{global: true, sym: map[string]*symbol{ 423 // predefined Go types 424 "bool": {kind: typeSym, typ: &itype{cat: boolT, name: "bool", str: "bool"}}, 425 "byte": {kind: typeSym, typ: &itype{cat: uint8T, name: "uint8", str: "uint8"}}, 426 "complex64": {kind: typeSym, typ: &itype{cat: complex64T, name: "complex64", str: "complex64"}}, 427 "complex128": {kind: typeSym, typ: &itype{cat: complex128T, name: "complex128", str: "complex128"}}, 428 "error": {kind: typeSym, typ: &itype{cat: errorT, name: "error", str: "error"}}, 429 "float32": {kind: typeSym, typ: &itype{cat: float32T, name: "float32", str: "float32"}}, 430 "float64": {kind: typeSym, typ: &itype{cat: float64T, name: "float64", str: "float64"}}, 431 "int": {kind: typeSym, typ: &itype{cat: intT, name: "int", str: "int"}}, 432 "int8": {kind: typeSym, typ: &itype{cat: int8T, name: "int8", str: "int8"}}, 433 "int16": {kind: typeSym, typ: &itype{cat: int16T, name: "int16", str: "int16"}}, 434 "int32": {kind: typeSym, typ: &itype{cat: int32T, name: "int32", str: "int32"}}, 435 "int64": {kind: typeSym, typ: &itype{cat: int64T, name: "int64", str: "int64"}}, 436 "interface{}": {kind: typeSym, typ: &itype{cat: interfaceT, str: "interface{}"}}, 437 "rune": {kind: typeSym, typ: &itype{cat: int32T, name: "int32", str: "int32"}}, 438 "string": {kind: typeSym, typ: &itype{cat: stringT, name: "string", str: "string"}}, 439 "uint": {kind: typeSym, typ: &itype{cat: uintT, name: "uint", str: "uint"}}, 440 "uint8": {kind: typeSym, typ: &itype{cat: uint8T, name: "uint8", str: "uint8"}}, 441 "uint16": {kind: typeSym, typ: &itype{cat: uint16T, name: "uint16", str: "uint16"}}, 442 "uint32": {kind: typeSym, typ: &itype{cat: uint32T, name: "uint32", str: "uint32"}}, 443 "uint64": {kind: typeSym, typ: &itype{cat: uint64T, name: "uint64", str: "uint64"}}, 444 "uintptr": {kind: typeSym, typ: &itype{cat: uintptrT, name: "uintptr", str: "uintptr"}}, 445 446 // predefined Go constants 447 "false": {kind: constSym, typ: untypedBool(), rval: reflect.ValueOf(false)}, 448 "true": {kind: constSym, typ: untypedBool(), rval: reflect.ValueOf(true)}, 449 "iota": {kind: constSym, typ: untypedInt()}, 450 451 // predefined Go zero value 452 "nil": {typ: &itype{cat: nilT, untyped: true, str: "nil"}}, 453 454 // predefined Go builtins 455 bltnAppend: {kind: bltnSym, builtin: _append}, 456 bltnCap: {kind: bltnSym, builtin: _cap}, 457 bltnClose: {kind: bltnSym, builtin: _close}, 458 bltnComplex: {kind: bltnSym, builtin: _complex}, 459 bltnImag: {kind: bltnSym, builtin: _imag}, 460 bltnCopy: {kind: bltnSym, builtin: _copy}, 461 bltnDelete: {kind: bltnSym, builtin: _delete}, 462 bltnLen: {kind: bltnSym, builtin: _len}, 463 bltnMake: {kind: bltnSym, builtin: _make}, 464 bltnNew: {kind: bltnSym, builtin: _new}, 465 bltnPanic: {kind: bltnSym, builtin: _panic}, 466 bltnPrint: {kind: bltnSym, builtin: _print}, 467 bltnPrintln: {kind: bltnSym, builtin: _println}, 468 bltnReal: {kind: bltnSym, builtin: _real}, 469 bltnRecover: {kind: bltnSym, builtin: _recover}, 470 }} 471 return sc 472 } 473 474 // resizeFrame resizes the global frame of interpreter. 475 func (interp *Interpreter) resizeFrame() { 476 l := len(interp.universe.types) 477 b := len(interp.frame.data) 478 if l-b <= 0 { 479 return 480 } 481 data := make([]reflect.Value, l) 482 copy(data, interp.frame.data) 483 for j, t := range interp.universe.types[b:] { 484 data[b+j] = reflect.New(t).Elem() 485 } 486 interp.frame.data = data 487 } 488 489 // Eval evaluates Go code represented as a string. Eval returns the last result 490 // computed by the interpreter, and a non nil error in case of failure. 491 func (interp *Interpreter) Eval(src string) (res reflect.Value, err error) { 492 return interp.eval(src, "", true) 493 } 494 495 // EvalPath evaluates Go code located at path and returns the last result computed 496 // by the interpreter, and a non nil error in case of failure. 497 // The main function of the main package is executed if present. 498 func (interp *Interpreter) EvalPath(path string) (res reflect.Value, err error) { 499 if !isFile(interp.opt.filesystem, path) { 500 _, err := interp.importSrc(mainID, path, NoTest) 501 return res, err 502 } 503 504 b, err := fs.ReadFile(interp.filesystem, path) 505 if err != nil { 506 return res, err 507 } 508 return interp.eval(string(b), path, false) 509 } 510 511 // EvalPathWithContext evaluates Go code located at path and returns the last 512 // result computed by the interpreter, and a non nil error in case of failure. 513 // The main function of the main package is executed if present. 514 func (interp *Interpreter) EvalPathWithContext(ctx context.Context, path string) (res reflect.Value, err error) { 515 interp.mutex.Lock() 516 interp.done = make(chan struct{}) 517 interp.cancelChan = !interp.opt.fastChan 518 interp.mutex.Unlock() 519 520 done := make(chan struct{}) 521 go func() { 522 defer close(done) 523 res, err = interp.EvalPath(path) 524 }() 525 526 select { 527 case <-ctx.Done(): 528 interp.stop() 529 return reflect.Value{}, ctx.Err() 530 case <-done: 531 } 532 return res, err 533 } 534 535 // EvalTest evaluates Go code located at path, including test files with "_test.go" suffix. 536 // A non nil error is returned in case of failure. 537 // The main function, test functions and benchmark functions are internally compiled but not 538 // executed. Test functions can be retrieved using the Symbol() method. 539 func (interp *Interpreter) EvalTest(path string) error { 540 _, err := interp.importSrc(mainID, path, Test) 541 return err 542 } 543 544 // Symbols returns a map of interpreter exported symbol values for the given 545 // import path. If the argument is the empty string, all known symbols are 546 // returned. 547 func (interp *Interpreter) Symbols(importPath string) Exports { 548 m := map[string]map[string]reflect.Value{} 549 interp.mutex.RLock() 550 defer interp.mutex.RUnlock() 551 552 for k, v := range interp.srcPkg { 553 if importPath != "" && k != importPath { 554 continue 555 } 556 syms := map[string]reflect.Value{} 557 for n, s := range v { 558 if !canExport(n) { 559 // Skip private non-exported symbols. 560 continue 561 } 562 switch s.kind { 563 case constSym: 564 syms[n] = s.rval 565 case funcSym: 566 syms[n] = genFunctionWrapper(s.node)(interp.frame) 567 case varSym: 568 syms[n] = interp.frame.data[s.index] 569 case typeSym: 570 syms[n] = reflect.New(s.typ.TypeOf()) 571 } 572 } 573 574 if len(syms) > 0 { 575 m[k] = syms 576 } 577 578 if importPath != "" { 579 return m 580 } 581 } 582 583 if importPath != "" && len(m) > 0 { 584 return m 585 } 586 587 for k, v := range interp.binPkg { 588 if importPath != "" && k != importPath { 589 continue 590 } 591 m[k] = v 592 if importPath != "" { 593 return m 594 } 595 } 596 597 return m 598 } 599 600 func isFile(filesystem fs.FS, path string) bool { 601 fi, err := fs.Stat(filesystem, path) 602 return err == nil && fi.Mode().IsRegular() 603 } 604 605 func (interp *Interpreter) eval(src, name string, inc bool) (res reflect.Value, err error) { 606 prog, err := interp.compileSrc(src, name, inc) 607 if err != nil { 608 return res, err 609 } 610 611 if interp.noRun { 612 return res, err 613 } 614 615 return interp.Execute(prog) 616 } 617 618 // EvalWithContext evaluates Go code represented as a string. It returns 619 // a map on current interpreted package exported symbols. 620 func (interp *Interpreter) EvalWithContext(ctx context.Context, src string) (reflect.Value, error) { 621 var v reflect.Value 622 var err error 623 624 interp.mutex.Lock() 625 interp.done = make(chan struct{}) 626 interp.cancelChan = !interp.opt.fastChan 627 interp.mutex.Unlock() 628 629 done := make(chan struct{}) 630 go func() { 631 defer close(done) 632 v, err = interp.Eval(src) 633 }() 634 635 select { 636 case <-ctx.Done(): 637 interp.stop() 638 return reflect.Value{}, ctx.Err() 639 case <-done: 640 } 641 return v, err 642 } 643 644 // stop sends a semaphore to all running frames and closes the chan 645 // operation short circuit channel. stop may only be called once per 646 // invocation of EvalWithContext. 647 func (interp *Interpreter) stop() { 648 atomic.AddUint64(&interp.id, 1) 649 close(interp.done) 650 } 651 652 func (interp *Interpreter) runid() uint64 { return atomic.LoadUint64(&interp.id) } 653 654 // getWrapper returns the wrapper type of the corresponding interface, or nil if not found. 655 func (interp *Interpreter) getWrapper(t reflect.Type) reflect.Type { 656 if p, ok := interp.binPkg[t.PkgPath()]; ok { 657 return p["_"+t.Name()].Type().Elem() 658 } 659 return nil 660 } 661 662 // Use loads binary runtime symbols in the interpreter context so 663 // they can be used in interpreted code. 664 func (interp *Interpreter) Use(values Exports) error { 665 for k, v := range values { 666 importPath := path.Dir(k) 667 packageName := path.Base(k) 668 669 if importPath == "." { 670 return fmt.Errorf("export path %[1]q is missing a package name; did you mean '%[1]s/%[1]s'?", k) 671 } 672 673 if importPath == selfPrefix { 674 interp.hooks.Parse(v) 675 continue 676 } 677 678 if interp.binPkg[importPath] == nil { 679 interp.binPkg[importPath] = make(map[string]reflect.Value) 680 interp.pkgNames[importPath] = packageName 681 } 682 683 for s, sym := range v { 684 interp.binPkg[importPath][s] = sym 685 } 686 if k == selfPath { 687 interp.binPkg[importPath]["Self"] = reflect.ValueOf(interp) 688 } 689 } 690 691 // Checks if input values correspond to stdlib packages by looking for one 692 // well known stdlib package path. 693 if _, ok := values["fmt/fmt"]; ok { 694 fixStdlib(interp) 695 } 696 return nil 697 } 698 699 // fixStdlib redefines interpreter stdlib symbols to use the standard input, 700 // output and errror assigned to the interpreter. The changes are limited to 701 // the interpreter only. 702 // Note that it is possible to escape the virtualized stdio by 703 // read/write directly to file descriptors 0, 1, 2. 704 func fixStdlib(interp *Interpreter) { 705 p := interp.binPkg["fmt"] 706 if p == nil { 707 return 708 } 709 710 stdin, stdout, stderr := interp.stdin, interp.stdout, interp.stderr 711 712 p["Print"] = reflect.ValueOf(func(a ...interface{}) (n int, err error) { return fmt.Fprint(stdout, a...) }) 713 p["Printf"] = reflect.ValueOf(func(f string, a ...interface{}) (n int, err error) { return fmt.Fprintf(stdout, f, a...) }) 714 p["Println"] = reflect.ValueOf(func(a ...interface{}) (n int, err error) { return fmt.Fprintln(stdout, a...) }) 715 716 p["Scan"] = reflect.ValueOf(func(a ...interface{}) (n int, err error) { return fmt.Fscan(stdin, a...) }) 717 p["Scanf"] = reflect.ValueOf(func(f string, a ...interface{}) (n int, err error) { return fmt.Fscanf(stdin, f, a...) }) 718 p["Scanln"] = reflect.ValueOf(func(a ...interface{}) (n int, err error) { return fmt.Fscanln(stdin, a...) }) 719 720 if p = interp.binPkg["flag"]; p != nil { 721 c := flag.NewFlagSet(os.Args[0], flag.PanicOnError) 722 c.SetOutput(stderr) 723 p["CommandLine"] = reflect.ValueOf(&c).Elem() 724 } 725 726 if p = interp.binPkg["log"]; p != nil { 727 l := log.New(stderr, "", log.LstdFlags) 728 // Restrict Fatal symbols to panic instead of exit. 729 p["Fatal"] = reflect.ValueOf(l.Panic) 730 p["Fatalf"] = reflect.ValueOf(l.Panicf) 731 p["Fatalln"] = reflect.ValueOf(l.Panicln) 732 733 p["Flags"] = reflect.ValueOf(l.Flags) 734 p["Output"] = reflect.ValueOf(l.Output) 735 p["Panic"] = reflect.ValueOf(l.Panic) 736 p["Panicf"] = reflect.ValueOf(l.Panicf) 737 p["Panicln"] = reflect.ValueOf(l.Panicln) 738 p["Prefix"] = reflect.ValueOf(l.Prefix) 739 p["Print"] = reflect.ValueOf(l.Print) 740 p["Printf"] = reflect.ValueOf(l.Printf) 741 p["Println"] = reflect.ValueOf(l.Println) 742 p["SetFlags"] = reflect.ValueOf(l.SetFlags) 743 p["SetOutput"] = reflect.ValueOf(l.SetOutput) 744 p["SetPrefix"] = reflect.ValueOf(l.SetPrefix) 745 p["Writer"] = reflect.ValueOf(l.Writer) 746 } 747 748 if p = interp.binPkg["os"]; p != nil { 749 p["Args"] = reflect.ValueOf(&interp.args).Elem() 750 if interp.specialStdio { 751 // Inherit streams from interpreter even if they do not have a file descriptor. 752 p["Stdin"] = reflect.ValueOf(&stdin).Elem() 753 p["Stdout"] = reflect.ValueOf(&stdout).Elem() 754 p["Stderr"] = reflect.ValueOf(&stderr).Elem() 755 } else { 756 // Inherits streams from interpreter only if they have a file descriptor and preserve original type. 757 if s, ok := stdin.(*os.File); ok { 758 p["Stdin"] = reflect.ValueOf(&s).Elem() 759 } 760 if s, ok := stdout.(*os.File); ok { 761 p["Stdout"] = reflect.ValueOf(&s).Elem() 762 } 763 if s, ok := stderr.(*os.File); ok { 764 p["Stderr"] = reflect.ValueOf(&s).Elem() 765 } 766 } 767 if !interp.unrestricted { 768 // In restricted mode, scripts can only access to a passed virtualized env, and can not write the real one. 769 getenv := func(key string) string { return interp.env[key] } 770 p["Clearenv"] = reflect.ValueOf(func() { interp.env = map[string]string{} }) 771 p["ExpandEnv"] = reflect.ValueOf(func(s string) string { return os.Expand(s, getenv) }) 772 p["Getenv"] = reflect.ValueOf(getenv) 773 p["LookupEnv"] = reflect.ValueOf(func(key string) (s string, ok bool) { s, ok = interp.env[key]; return }) 774 p["Setenv"] = reflect.ValueOf(func(key, value string) error { interp.env[key] = value; return nil }) 775 p["Unsetenv"] = reflect.ValueOf(func(key string) error { delete(interp.env, key); return nil }) 776 p["Environ"] = reflect.ValueOf(func() (a []string) { 777 for k, v := range interp.env { 778 a = append(a, k+"="+v) 779 } 780 return 781 }) 782 } 783 } 784 785 if p = interp.binPkg["math/bits"]; p != nil { 786 // Do not trust extracted value maybe from another arch. 787 p["UintSize"] = reflect.ValueOf(constant.MakeInt64(bits.UintSize)) 788 } 789 } 790 791 // ignoreScannerError returns true if the error from Go scanner can be safely ignored 792 // to let the caller grab one more line before retrying to parse its input. 793 func ignoreScannerError(e *scanner.Error, s string) bool { 794 msg := e.Msg 795 if strings.HasSuffix(msg, "found 'EOF'") { 796 return true 797 } 798 if msg == "raw string literal not terminated" { 799 return true 800 } 801 if strings.HasPrefix(msg, "expected operand, found '}'") && !strings.HasSuffix(s, "}") { 802 return true 803 } 804 return false 805 } 806 807 // ImportUsed automatically imports pre-compiled packages included by Use(). 808 // This is mainly useful for REPLs, or single command lines. In case of an ambiguous default 809 // package name, for example "rand" for crypto/rand and math/rand, the package name is 810 // constructed by replacing the last "/" by a "_", producing crypto_rand and math_rand. 811 // ImportUsed should not be called more than once, and not after a first Eval, as it may 812 // rename packages. 813 func (interp *Interpreter) ImportUsed() { 814 sc := interp.universe 815 for k := range interp.binPkg { 816 // By construction, the package name is the last path element of the key. 817 name := path.Base(k) 818 if sym, ok := sc.sym[name]; ok { 819 // Handle collision by renaming old and new entries. 820 name2 := key2name(fixKey(sym.typ.path)) 821 sc.sym[name2] = sym 822 if name2 != name { 823 delete(sc.sym, name) 824 } 825 name = key2name(fixKey(k)) 826 } 827 sc.sym[name] = &symbol{kind: pkgSym, typ: &itype{cat: binPkgT, path: k, scope: sc}} 828 } 829 } 830 831 func key2name(name string) string { 832 return filepath.Join(name, DefaultSourceName) 833 } 834 835 func fixKey(k string) string { 836 i := strings.LastIndex(k, "/") 837 if i >= 0 { 838 k = k[:i] + "_" + k[i+1:] 839 } 840 return k 841 } 842 843 // REPL performs a Read-Eval-Print-Loop on input reader. 844 // Results are printed to the output writer of the Interpreter, provided as option 845 // at creation time. Errors are printed to the similarly defined errors writer. 846 // The last interpreter result value and error are returned. 847 func (interp *Interpreter) REPL() (reflect.Value, error) { 848 in, out, errs := interp.stdin, interp.stdout, interp.stderr 849 ctx, cancel := context.WithCancel(context.Background()) 850 end := make(chan struct{}) // channel to terminate the REPL 851 sig := make(chan os.Signal, 1) // channel to trap interrupt signal (Ctrl-C) 852 lines := make(chan string) // channel to read REPL input lines 853 prompt := getPrompt(in, out) // prompt activated on tty like IO stream 854 s := bufio.NewScanner(in) // read input stream line by line 855 var v reflect.Value // result value from eval 856 var err error // error from eval 857 src := "" // source string to evaluate 858 859 signal.Notify(sig, os.Interrupt) 860 defer signal.Stop(sig) 861 prompt(v) 862 863 go func() { 864 defer close(end) 865 for s.Scan() { 866 lines <- s.Text() 867 } 868 if e := s.Err(); e != nil { 869 fmt.Fprintln(errs, e) 870 } 871 }() 872 873 go func() { 874 for { 875 select { 876 case <-sig: 877 cancel() 878 lines <- "" 879 case <-end: 880 return 881 } 882 } 883 }() 884 885 for { 886 var line string 887 888 select { 889 case <-end: 890 cancel() 891 return v, err 892 case line = <-lines: 893 src += line + "\n" 894 } 895 896 v, err = interp.EvalWithContext(ctx, src) 897 if err != nil { 898 switch e := err.(type) { 899 case scanner.ErrorList: 900 if len(e) > 0 && ignoreScannerError(e[0], line) { 901 continue 902 } 903 fmt.Fprintln(errs, strings.TrimPrefix(e[0].Error(), DefaultSourceName+":")) 904 case Panic: 905 fmt.Fprintln(errs, e.Value) 906 fmt.Fprintln(errs, string(e.Stack)) 907 default: 908 fmt.Fprintln(errs, err) 909 } 910 } 911 if errors.Is(err, context.Canceled) { 912 ctx, cancel = context.WithCancel(context.Background()) 913 } 914 src = "" 915 prompt(v) 916 } 917 } 918 919 func doPrompt(out io.Writer) func(v reflect.Value) { 920 return func(v reflect.Value) { 921 if v.IsValid() { 922 fmt.Fprintln(out, ":", v) 923 } 924 fmt.Fprint(out, "> ") 925 } 926 } 927 928 // getPrompt returns a function which prints a prompt only if input is a terminal. 929 func getPrompt(in io.Reader, out io.Writer) func(reflect.Value) { 930 forcePrompt, _ := strconv.ParseBool(os.Getenv("YAEGI_PROMPT")) 931 if forcePrompt { 932 return doPrompt(out) 933 } 934 s, ok := in.(interface{ Stat() (os.FileInfo, error) }) 935 if !ok { 936 return func(reflect.Value) {} 937 } 938 stat, err := s.Stat() 939 if err == nil && stat.Mode()&os.ModeCharDevice != 0 { 940 return doPrompt(out) 941 } 942 return func(reflect.Value) {} 943 }