Update Interpreter.Use API
### Background #1102 changed how `Interpreter.Use` interprets export paths such that the last path component is stripped and used as the package name. This resulted in #1139 - attempting to Use an export with only one path component, such as `foo`, would result in the import path being `.`. ### Breaking API Change This PR changes the signature of `Interpreter.Use` from `Use(Exports)` to `Use(Exports) error`. ### Fix for #1139 With this PR, if Use is called with an incomplete export path, such as `foo`, Use will return an error.
This commit is contained in:
@@ -48,24 +48,34 @@ func run(arg []string) error {
|
||||
args := rflag.Args()
|
||||
|
||||
i := interp.New(interp.Options{GoPath: build.Default.GOPATH, BuildTags: strings.Split(tags, ",")})
|
||||
i.Use(stdlib.Symbols)
|
||||
i.Use(interp.Symbols)
|
||||
if err := i.Use(stdlib.Symbols); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := i.Use(interp.Symbols); err != nil {
|
||||
return err
|
||||
}
|
||||
if useSyscall {
|
||||
i.Use(syscall.Symbols)
|
||||
if err := i.Use(syscall.Symbols); err != nil {
|
||||
return err
|
||||
}
|
||||
// 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 {
|
||||
i.Use(unsafe.Symbols)
|
||||
if err := i.Use(unsafe.Symbols); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.Setenv("YAEGI_UNSAFE", "1"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if useUnrestricted {
|
||||
// Use of unrestricted symbols should always follow stdlib and syscall symbols, to update them.
|
||||
i.Use(unrestricted.Symbols)
|
||||
if err := i.Use(unrestricted.Symbols); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.Setenv("YAEGI_UNRESTRICTED", "1"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -117,23 +117,33 @@ func test(arg []string) (err error) {
|
||||
}
|
||||
|
||||
i := interp.New(interp.Options{GoPath: build.Default.GOPATH, BuildTags: strings.Split(tags, ",")})
|
||||
i.Use(stdlib.Symbols)
|
||||
i.Use(interp.Symbols)
|
||||
if err := i.Use(stdlib.Symbols); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := i.Use(interp.Symbols); err != nil {
|
||||
return err
|
||||
}
|
||||
if useSyscall {
|
||||
i.Use(syscall.Symbols)
|
||||
if err := i.Use(syscall.Symbols); err != nil {
|
||||
return err
|
||||
}
|
||||
// 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 useUnrestricted {
|
||||
i.Use(unrestricted.Symbols)
|
||||
if err := i.Use(unrestricted.Symbols); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.Setenv("YAEGI_UNRESTRICTED", "1"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if useUnsafe {
|
||||
i.Use(unsafe.Symbols)
|
||||
if err := i.Use(unsafe.Symbols); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.Setenv("YAEGI_UNSAFE", "1"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -9,7 +9,9 @@ import (
|
||||
|
||||
func TestFunctionCall(t *testing.T) {
|
||||
i := interp.New(interp.Options{GoPath: "./_pkg"})
|
||||
i.Use(stdlib.Symbols)
|
||||
if err := i.Use(stdlib.Symbols); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err := i.Eval(`import "foo/bar"`)
|
||||
if err != nil {
|
||||
|
||||
@@ -10,7 +10,9 @@ import (
|
||||
|
||||
func TestGetFunc(t *testing.T) {
|
||||
i := interp.New(interp.Options{GoPath: "./_gopath/"})
|
||||
i.Use(stdlib.Symbols)
|
||||
if err := i.Use(stdlib.Symbols); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if _, err := i.Eval(`import "github.com/foo/bar"`); err != nil {
|
||||
t.Fatal(err)
|
||||
|
||||
@@ -111,7 +111,10 @@ func TestPackages(t *testing.T) {
|
||||
|
||||
var stdout, stderr bytes.Buffer
|
||||
i := interp.New(interp.Options{GoPath: goPath, Stdout: &stdout, Stderr: &stderr})
|
||||
i.Use(stdlib.Symbols) // Use binary standard library
|
||||
// Use binary standard library
|
||||
if err := i.Use(stdlib.Symbols); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var msg string
|
||||
if test.evalFile != "" {
|
||||
@@ -170,7 +173,10 @@ func TestPackagesError(t *testing.T) {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
// Init go interpreter
|
||||
i := interp.New(interp.Options{GoPath: test.goPath})
|
||||
i.Use(stdlib.Symbols) // Use binary standard library
|
||||
// Use binary standard library
|
||||
if err := i.Use(stdlib.Symbols); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Load pkg from sources
|
||||
_, err := i.Eval(`import "github.com/foo/pkg"`)
|
||||
|
||||
@@ -653,11 +653,15 @@ func (interp *Interpreter) getWrapper(t reflect.Type) reflect.Type {
|
||||
|
||||
// Use loads binary runtime symbols in the interpreter context so
|
||||
// they can be used in interpreted code.
|
||||
func (interp *Interpreter) Use(values Exports) {
|
||||
func (interp *Interpreter) Use(values Exports) error {
|
||||
for k, v := range values {
|
||||
importPath := path.Dir(k)
|
||||
packageName := path.Base(k)
|
||||
|
||||
if importPath == "." {
|
||||
return fmt.Errorf("export path %[1]q is missing a package name; did you mean '%[1]s/%[1]s'?", k)
|
||||
}
|
||||
|
||||
if importPath == selfPrefix {
|
||||
interp.hooks.Parse(v)
|
||||
continue
|
||||
@@ -681,6 +685,7 @@ func (interp *Interpreter) Use(values Exports) {
|
||||
if _, ok := values["fmt/fmt"]; ok {
|
||||
fixStdio(interp)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// fixStdio redefines interpreter stdlib symbols to use the standard input,
|
||||
|
||||
@@ -115,9 +115,15 @@ func TestInterpConsistencyBuild(t *testing.T) {
|
||||
os.Stdout = w
|
||||
|
||||
i := interp.New(interp.Options{GoPath: build.Default.GOPATH})
|
||||
i.Use(stdlib.Symbols)
|
||||
i.Use(interp.Symbols)
|
||||
i.Use(unsafe.Symbols)
|
||||
if err := i.Use(stdlib.Symbols); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := i.Use(interp.Symbols); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := i.Use(unsafe.Symbols); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = i.EvalPath(filePath)
|
||||
if err != nil {
|
||||
@@ -260,7 +266,9 @@ func TestInterpErrorConsistency(t *testing.T) {
|
||||
filePath := filepath.Join("..", "_test", test.fileName)
|
||||
|
||||
i := interp.New(interp.Options{GoPath: build.Default.GOPATH})
|
||||
i.Use(stdlib.Symbols)
|
||||
if err := i.Use(stdlib.Symbols); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, errEval := i.EvalPath(filePath)
|
||||
if errEval == nil {
|
||||
|
||||
@@ -103,11 +103,14 @@ func TestEvalStar(t *testing.T) {
|
||||
|
||||
func TestEvalAssign(t *testing.T) {
|
||||
i := interp.New(interp.Options{})
|
||||
i.Use(interp.Exports{
|
||||
if err := i.Use(interp.Exports{
|
||||
"testpkg/testpkg": {
|
||||
"val": reflect.ValueOf(int64(11)),
|
||||
},
|
||||
})
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, e := i.Eval(`import "testpkg"`)
|
||||
if e != nil {
|
||||
t.Fatal(e)
|
||||
@@ -205,7 +208,9 @@ func TestEvalFunc(t *testing.T) {
|
||||
|
||||
func TestEvalImport(t *testing.T) {
|
||||
i := interp.New(interp.Options{})
|
||||
i.Use(stdlib.Symbols)
|
||||
if err := i.Use(stdlib.Symbols); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
runTests(t, i, []testCase{
|
||||
{pre: func() { eval(t, i, `import "time"`) }, src: "2 * time.Second", res: "2s"},
|
||||
})
|
||||
@@ -214,7 +219,9 @@ func TestEvalImport(t *testing.T) {
|
||||
func TestEvalStdout(t *testing.T) {
|
||||
var out, err bytes.Buffer
|
||||
i := interp.New(interp.Options{Stdout: &out, Stderr: &err})
|
||||
i.Use(stdlib.Symbols)
|
||||
if err := i.Use(stdlib.Symbols); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, e := i.Eval(`import "fmt"; func main() { fmt.Println("hello") }`)
|
||||
if e != nil {
|
||||
t.Fatal(e)
|
||||
@@ -227,7 +234,9 @@ func TestEvalStdout(t *testing.T) {
|
||||
|
||||
func TestEvalNil(t *testing.T) {
|
||||
i := interp.New(interp.Options{})
|
||||
i.Use(stdlib.Symbols)
|
||||
if err := i.Use(stdlib.Symbols); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
runTests(t, i, []testCase{
|
||||
{desc: "assign nil", src: "a := nil", err: "1:33: use of untyped nil"},
|
||||
{desc: "return nil", pre: func() { eval(t, i, "func getNil() error {return nil}") }, src: "getNil()", res: "<nil>"},
|
||||
@@ -375,7 +384,9 @@ var a = T{
|
||||
|
||||
func TestEvalCompositeBin0(t *testing.T) {
|
||||
i := interp.New(interp.Options{})
|
||||
i.Use(stdlib.Symbols)
|
||||
if err := i.Use(stdlib.Symbols); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
eval(t, i, `
|
||||
import (
|
||||
"fmt"
|
||||
@@ -636,7 +647,9 @@ func TestEvalCall(t *testing.T) {
|
||||
|
||||
func TestEvalBinCall(t *testing.T) {
|
||||
i := interp.New(interp.Options{})
|
||||
i.Use(stdlib.Symbols)
|
||||
if err := i.Use(stdlib.Symbols); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := i.Eval(`import "fmt"`); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -662,9 +675,11 @@ func TestEvalMissingSymbol(t *testing.T) {
|
||||
F S2
|
||||
}
|
||||
i := interp.New(interp.Options{})
|
||||
i.Use(interp.Exports{"p/p": map[string]reflect.Value{
|
||||
if err := i.Use(interp.Exports{"p/p": map[string]reflect.Value{
|
||||
"S1": reflect.Zero(reflect.TypeOf(&S1{})),
|
||||
}})
|
||||
}}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, err := i.Eval(`import "p"`)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to import package: %v", err)
|
||||
@@ -733,7 +748,9 @@ func TestEvalWithContext(t *testing.T) {
|
||||
go func() {
|
||||
defer close(done)
|
||||
i := interp.New(interp.Options{})
|
||||
i.Use(stdlib.Symbols)
|
||||
if err := i.Use(stdlib.Symbols); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
_, err := i.Eval(`import "sync"`)
|
||||
if err != nil {
|
||||
t.Errorf(`failed to import "sync": %v`, err)
|
||||
@@ -836,8 +853,9 @@ func TestMultiEval(t *testing.T) {
|
||||
os.Stdout = w
|
||||
|
||||
i := interp.New(interp.Options{})
|
||||
i.Use(stdlib.Symbols)
|
||||
var err error
|
||||
if err := i.Use(stdlib.Symbols); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
f, err := os.Open(filepath.Join("testdata", "multi", "731"))
|
||||
if err != nil {
|
||||
@@ -875,8 +893,9 @@ func TestMultiEval(t *testing.T) {
|
||||
func TestMultiEvalNoName(t *testing.T) {
|
||||
t.Skip("fail in CI only ?")
|
||||
i := interp.New(interp.Options{})
|
||||
i.Use(stdlib.Symbols)
|
||||
var err error
|
||||
if err := i.Use(stdlib.Symbols); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
f, err := os.Open(filepath.Join("testdata", "multi", "731"))
|
||||
if err != nil {
|
||||
@@ -908,7 +927,9 @@ func TestMultiEvalNoName(t *testing.T) {
|
||||
func TestImportPathIsKey(t *testing.T) {
|
||||
// No need to check the results of Eval, as TestFile already does it.
|
||||
i := interp.New(interp.Options{GoPath: filepath.FromSlash("../_test/testdata/redeclaration-global7")})
|
||||
i.Use(stdlib.Symbols)
|
||||
if err := i.Use(stdlib.Symbols); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
filePath := filepath.Join("..", "_test", "ipp_as_key.go")
|
||||
if _, err := i.EvalPath(filePath); err != nil {
|
||||
@@ -987,7 +1008,9 @@ func TestConcurrentEvals(t *testing.T) {
|
||||
_ = pout.Close()
|
||||
}()
|
||||
interpr := interp.New(interp.Options{Stdout: pout})
|
||||
interpr.Use(stdlib.Symbols)
|
||||
if err := interpr.Use(stdlib.Symbols); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if _, err := interpr.EvalPath("testdata/concurrent/hello1.go"); err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -1046,7 +1069,9 @@ func TestConcurrentEvals2(t *testing.T) {
|
||||
_ = pout.Close()
|
||||
}()
|
||||
interpr := interp.New(interp.Options{Stdout: pout})
|
||||
interpr.Use(stdlib.Symbols)
|
||||
if err := interpr.Use(stdlib.Symbols); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
done := make(chan error)
|
||||
go func() {
|
||||
@@ -1108,7 +1133,9 @@ func TestConcurrentEvals3(t *testing.T) {
|
||||
pinin, poutin := io.Pipe()
|
||||
pinout, poutout := io.Pipe()
|
||||
i := interp.New(interp.Options{Stdin: pinin, Stdout: poutout})
|
||||
i.Use(stdlib.Symbols)
|
||||
if err := i.Use(stdlib.Symbols); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
go func() {
|
||||
_, _ = i.REPL()
|
||||
@@ -1187,7 +1214,9 @@ func testConcurrentComposite(t *testing.T, filePath string) {
|
||||
}
|
||||
pin, pout := io.Pipe()
|
||||
i := interp.New(interp.Options{Stdout: pout})
|
||||
i.Use(stdlib.Symbols)
|
||||
if err := i.Use(stdlib.Symbols); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
errc := make(chan error)
|
||||
var output string
|
||||
@@ -1411,7 +1440,9 @@ func TestREPLCommands(t *testing.T) {
|
||||
pinin, poutin := io.Pipe()
|
||||
pinout, poutout := io.Pipe()
|
||||
i := interp.New(interp.Options{Stdin: pinin, Stdout: poutout})
|
||||
i.Use(stdlib.Symbols)
|
||||
if err := i.Use(stdlib.Symbols); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
go func() {
|
||||
_, _ = i.REPL()
|
||||
@@ -1494,7 +1525,9 @@ func TestREPLCommands(t *testing.T) {
|
||||
|
||||
func TestStdio(t *testing.T) {
|
||||
i := interp.New(interp.Options{})
|
||||
i.Use(stdlib.Symbols)
|
||||
if err := i.Use(stdlib.Symbols); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
i.ImportUsed()
|
||||
if _, err := i.Eval(`var x = os.Stdout`); err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -1519,11 +1552,13 @@ func (v *Issue1149Array) Bar() string { return "foo" }
|
||||
|
||||
func TestIssue1149(t *testing.T) {
|
||||
i := interp.New(interp.Options{})
|
||||
i.Use(interp.Exports{
|
||||
if err := i.Use(interp.Exports{
|
||||
"pkg/pkg": map[string]reflect.Value{
|
||||
"Type": reflect.ValueOf((*Issue1149Array)(nil)),
|
||||
},
|
||||
})
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
i.ImportUsed()
|
||||
|
||||
_, err := i.Eval(`
|
||||
@@ -1577,12 +1612,14 @@ func TestIssue1151(t *testing.T) {
|
||||
type pkgArray [1]int
|
||||
|
||||
i := interp.New(interp.Options{})
|
||||
i.Use(interp.Exports{
|
||||
if err := i.Use(interp.Exports{
|
||||
"pkg/pkg": map[string]reflect.Value{
|
||||
"Struct": reflect.ValueOf((*pkgStruct)(nil)),
|
||||
"Array": reflect.ValueOf((*pkgArray)(nil)),
|
||||
},
|
||||
})
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
i.ImportUsed()
|
||||
|
||||
runTests(t, i, []testCase{
|
||||
|
||||
@@ -41,14 +41,53 @@ type Wrap struct {
|
||||
|
||||
func (w Wrap) Hello() { w.DoHello() }
|
||||
|
||||
func TestExportsSemantics(t *testing.T) {
|
||||
Foo := &struct{}{}
|
||||
|
||||
t.Run("Correct", func(t *testing.T) {
|
||||
t.Skip()
|
||||
i := interp.New(interp.Options{})
|
||||
|
||||
err := i.Use(interp.Exports{
|
||||
"foo/foo": {"Foo": reflect.ValueOf(Foo)},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
i.ImportUsed()
|
||||
|
||||
res, err := i.Eval("foo.Foo")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if res.Interface() != Foo {
|
||||
t.Fatalf("expected foo.Foo to equal local Foo")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Incorrect", func(t *testing.T) {
|
||||
i := interp.New(interp.Options{})
|
||||
|
||||
err := i.Use(interp.Exports{
|
||||
"foo": {"Foo": reflect.ValueOf(Foo)},
|
||||
})
|
||||
if err == nil {
|
||||
t.Fatal("expected error for incorrect Use semantics")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestInterface(t *testing.T) {
|
||||
i := interp.New(interp.Options{})
|
||||
// export the Wrap type to the interpreter under virtual "wrap" package
|
||||
i.Use(interp.Exports{
|
||||
err := i.Use(interp.Exports{
|
||||
"wrap/wrap": {
|
||||
"Wrap": reflect.ValueOf((*Wrap)(nil)),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
eval(t, i, `
|
||||
import "wrap"
|
||||
@@ -73,11 +112,14 @@ func (t T) Bar(s ...string) {}
|
||||
|
||||
func TestCallBinVariadicMethod(t *testing.T) {
|
||||
i := interp.New(interp.Options{})
|
||||
i.Use(interp.Exports{
|
||||
err := i.Use(interp.Exports{
|
||||
"mypkg/mypkg": {
|
||||
"T": reflect.ValueOf((*T)(nil)),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
eval(t, i, `
|
||||
package p
|
||||
|
||||
|
||||
@@ -55,9 +55,15 @@ func runCheck(t *testing.T, p string) {
|
||||
}
|
||||
var stdout, stderr bytes.Buffer
|
||||
i := interp.New(interp.Options{GoPath: goPath, Stdout: &stdout, Stderr: &stderr})
|
||||
i.Use(interp.Symbols)
|
||||
i.Use(stdlib.Symbols)
|
||||
i.Use(unsafe.Symbols)
|
||||
if err := i.Use(interp.Symbols); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := i.Use(stdlib.Symbols); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := i.Use(unsafe.Symbols); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err := i.EvalPath(p)
|
||||
if errWanted {
|
||||
|
||||
@@ -10,8 +10,12 @@ import (
|
||||
func ExampleInterpreter_self() {
|
||||
i := interp.New(interp.Options{})
|
||||
|
||||
i.Use(stdlib.Symbols)
|
||||
i.Use(interp.Symbols)
|
||||
if err := i.Use(stdlib.Symbols); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if err := i.Use(interp.Symbols); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
_, err := i.Eval(`import (
|
||||
"fmt"
|
||||
|
||||
Reference in New Issue
Block a user