fix: allow yaegi command to interpret itself
Since the introduction of restricted stdlib and syscall symbols, the capability of yaegi to interpret itself was broken. The use of unrestricted symbols is now also controlled by environment variables, to allow propagation accross nested interpreters. The interpreter Panic symbol was not wrapped, this is fixed now. the import path resolution was failing if the working directory was outside of GOPATH. The documentation and readme have been ajusted. Fixes #890.
This commit is contained in:
14
README.md
14
README.md
@@ -128,10 +128,20 @@ Hello World
|
|||||||
>
|
>
|
||||||
```
|
```
|
||||||
|
|
||||||
Or interpret Go files:
|
Note that in interactive mode, all stdlib package are pre-imported,
|
||||||
|
you can use them directly:
|
||||||
|
|
||||||
```console
|
```console
|
||||||
$ yaegi cmd/yaegi/yaegi.go
|
$ yaegi
|
||||||
|
> reflect.TypeOf(time.Date)
|
||||||
|
: func(int, time.Month, int, int, int, int, int, *time.Location) time.Time
|
||||||
|
>
|
||||||
|
```
|
||||||
|
|
||||||
|
Or interpret Go packages, directories or files, including itself:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ yaegi -syscall -unsafe -unrestricted github.com/traefik/yaegi/cmd/yaegi
|
||||||
>
|
>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"go/build"
|
"go/build"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/traefik/yaegi/interp"
|
"github.com/traefik/yaegi/interp"
|
||||||
@@ -17,19 +18,21 @@ import (
|
|||||||
|
|
||||||
func run(arg []string) error {
|
func run(arg []string) error {
|
||||||
var interactive bool
|
var interactive bool
|
||||||
var useSyscall bool
|
|
||||||
var useUnrestricted bool
|
|
||||||
var useUnsafe bool
|
|
||||||
var tags string
|
var tags string
|
||||||
var cmd string
|
var cmd string
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
|
// The following flags are initialized from environment.
|
||||||
|
useSyscall, _ := strconv.ParseBool(os.Getenv("YAEGI_SYSCALL"))
|
||||||
|
useUnrestricted, _ := strconv.ParseBool(os.Getenv("YAEGI_UNRESTRICTED"))
|
||||||
|
useUnsafe, _ := strconv.ParseBool(os.Getenv("YAEGI_UNSAFE"))
|
||||||
|
|
||||||
rflag := flag.NewFlagSet("run", flag.ContinueOnError)
|
rflag := flag.NewFlagSet("run", flag.ContinueOnError)
|
||||||
rflag.BoolVar(&interactive, "i", false, "start an interactive REPL")
|
rflag.BoolVar(&interactive, "i", false, "start an interactive REPL")
|
||||||
rflag.BoolVar(&useSyscall, "syscall", false, "include syscall symbols")
|
rflag.BoolVar(&useSyscall, "syscall", useSyscall, "include syscall symbols")
|
||||||
rflag.BoolVar(&useUnrestricted, "unrestricted", false, "include unrestricted symbols")
|
rflag.BoolVar(&useUnrestricted, "unrestricted", useUnrestricted, "include unrestricted symbols")
|
||||||
rflag.StringVar(&tags, "tags", "", "set a list of build tags")
|
rflag.StringVar(&tags, "tags", "", "set a list of build tags")
|
||||||
rflag.BoolVar(&useUnsafe, "unsafe", false, "include usafe symbols")
|
rflag.BoolVar(&useUnsafe, "unsafe", useUnsafe, "include unsafe symbols")
|
||||||
rflag.StringVar(&cmd, "e", "", "set the command to be executed (instead of script or/and shell)")
|
rflag.StringVar(&cmd, "e", "", "set the command to be executed (instead of script or/and shell)")
|
||||||
rflag.Usage = func() {
|
rflag.Usage = func() {
|
||||||
fmt.Println("Usage: yaegi run [options] [path] [args]")
|
fmt.Println("Usage: yaegi run [options] [path] [args]")
|
||||||
@@ -46,13 +49,23 @@ func run(arg []string) error {
|
|||||||
i.Use(interp.Symbols)
|
i.Use(interp.Symbols)
|
||||||
if useSyscall {
|
if useSyscall {
|
||||||
i.Use(syscall.Symbols)
|
i.Use(syscall.Symbols)
|
||||||
|
// Using a environment var allows a nested interpreter to import the syscall package.
|
||||||
|
if err := os.Setenv("YAEGI_SYSCALL", "1"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if useUnsafe {
|
if useUnsafe {
|
||||||
i.Use(unsafe.Symbols)
|
i.Use(unsafe.Symbols)
|
||||||
|
if err := os.Setenv("YAEGI_UNSAFE", "1"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if useUnrestricted {
|
if useUnrestricted {
|
||||||
// Use of unrestricted symbols should always follow stdlib and syscall symbols, to update them.
|
// Use of unrestricted symbols should always follow stdlib and syscall symbols, to update them.
|
||||||
i.Use(unrestricted.Symbols)
|
i.Use(unrestricted.Symbols)
|
||||||
|
if err := os.Setenv("YAEGI_UNRESTRICTED", "1"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if cmd != "" {
|
if cmd != "" {
|
||||||
|
|||||||
@@ -71,7 +71,13 @@ Options:
|
|||||||
-unsafe
|
-unsafe
|
||||||
include unsafe symbols.
|
include unsafe symbols.
|
||||||
|
|
||||||
Debugging support (may be removed at any time):
|
Environment variables:
|
||||||
|
YAEGI_SYSCALL=1
|
||||||
|
Include syscall symbols (same as -syscall flag).
|
||||||
|
YAEGI_UNRESTRICTED=1
|
||||||
|
Include unrestricted symbols (same as -unrestricted flag).
|
||||||
|
YAEGI_UNSAFE=1
|
||||||
|
Include unsafe symbols (same as -unsafe flag).
|
||||||
YAEGI_PROMPT=1
|
YAEGI_PROMPT=1
|
||||||
Force enable the printing of the REPL prompt and the result of last instruction,
|
Force enable the printing of the REPL prompt and the result of last instruction,
|
||||||
even if stdin is not a terminal.
|
even if stdin is not a terminal.
|
||||||
|
|||||||
@@ -2,8 +2,6 @@ package interp
|
|||||||
|
|
||||||
import "reflect"
|
import "reflect"
|
||||||
|
|
||||||
const hooksPath = "github.com/traefik/yaegi"
|
|
||||||
|
|
||||||
// convertFn is the signature of a symbol converter.
|
// convertFn is the signature of a symbol converter.
|
||||||
type convertFn func(from, to reflect.Type) func(src, dest reflect.Value)
|
type convertFn func(from, to reflect.Type) func(src, dest reflect.Value)
|
||||||
|
|
||||||
|
|||||||
@@ -155,8 +155,9 @@ type Interpreter struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
mainID = "main"
|
mainID = "main"
|
||||||
selfPath = "github.com/traefik/yaegi/interp"
|
selfPrefix = "github.com/traefik/yaegi"
|
||||||
|
selfPath = selfPrefix + "/interp"
|
||||||
// DefaultSourceName is the name used by default when the name of the input
|
// DefaultSourceName is the name used by default when the name of the input
|
||||||
// source file has not been specified for an Eval.
|
// source file has not been specified for an Eval.
|
||||||
// TODO(mpl): something even more special as a name?
|
// TODO(mpl): something even more special as a name?
|
||||||
@@ -175,6 +176,7 @@ var Symbols = Exports{
|
|||||||
|
|
||||||
"Interpreter": reflect.ValueOf((*Interpreter)(nil)),
|
"Interpreter": reflect.ValueOf((*Interpreter)(nil)),
|
||||||
"Options": reflect.ValueOf((*Options)(nil)),
|
"Options": reflect.ValueOf((*Options)(nil)),
|
||||||
|
"Panic": reflect.ValueOf((*Panic)(nil)),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -603,7 +605,7 @@ func (interp *Interpreter) getWrapper(t reflect.Type) reflect.Type {
|
|||||||
// they can be used in interpreted code.
|
// they can be used in interpreted code.
|
||||||
func (interp *Interpreter) Use(values Exports) {
|
func (interp *Interpreter) Use(values Exports) {
|
||||||
for k, v := range values {
|
for k, v := range values {
|
||||||
if k == hooksPath {
|
if k == selfPrefix {
|
||||||
interp.hooks.Parse(v)
|
interp.hooks.Parse(v)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2037,7 +2037,7 @@ func doCompositeBinStruct(n *node, hasType bool) {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
fieldIndex[i] = []int{i}
|
fieldIndex[i] = []int{i}
|
||||||
if c.typ.cat == funcT {
|
if c.typ.cat == funcT && len(c.child) > 1 {
|
||||||
convertLiteralValue(c.child[1], typ.Field(i).Type)
|
convertLiteralValue(c.child[1], typ.Field(i).Type)
|
||||||
values[i] = genFunctionWrapper(c.child[1])
|
values[i] = genFunctionWrapper(c.child[1])
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -33,17 +33,12 @@ func (interp *Interpreter) importSrc(rPath, importPath string, skipTest bool) (s
|
|||||||
rPath = "."
|
rPath = "."
|
||||||
}
|
}
|
||||||
dir = filepath.Join(filepath.Dir(interp.name), rPath, importPath)
|
dir = filepath.Join(filepath.Dir(interp.name), rPath, importPath)
|
||||||
} else {
|
} else if dir, rPath, err = pkgDir(interp.context.GOPATH, rPath, importPath); err != nil {
|
||||||
var root string
|
// Try again, assuming a root dir at the source location.
|
||||||
if rPath == mainID {
|
if rPath, err = interp.rootFromSourceLocation(); err != nil {
|
||||||
root, err = interp.rootFromSourceLocation()
|
return "", err
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
root = rPath
|
|
||||||
}
|
}
|
||||||
if dir, rPath, err = pkgDir(interp.context.GOPATH, root, importPath); err != nil {
|
if dir, rPath, err = pkgDir(interp.context.GOPATH, rPath, importPath); err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -559,7 +559,6 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err = n.cfgErrorf("undefined selector %s.%s", lt.path, name)
|
err = n.cfgErrorf("undefined selector %s.%s", lt.path, name)
|
||||||
panic(err)
|
|
||||||
}
|
}
|
||||||
case srcPkgT:
|
case srcPkgT:
|
||||||
pkg := interp.srcPkg[lt.path]
|
pkg := interp.srcPkg[lt.path]
|
||||||
|
|||||||
Reference in New Issue
Block a user