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 }