fix: reuse rather than re-import an already imported source package

This commit is contained in:
Marc Vertes
2019-09-09 15:54:05 +02:00
committed by Traefiker Bot
parent bfa9a267be
commit 2f0279f0f5
12 changed files with 67 additions and 21 deletions

View File

@@ -1,6 +1,8 @@
package foo
import "./boo"
import "github.com/containous/yaegi/_test/foo/boo"
var Bar = "BARR"
var Boo = boo.Boo
func init() { println("init foo") }

5
_test/foo/bir.go Normal file
View File

@@ -0,0 +1,5 @@
package foo
import "github.com/containous/yaegi/_test/foo/boo"
var Bir = boo.Boo + "22"

View File

@@ -1,3 +1,5 @@
package boo
var Boo = "Boo"
func init() { println("init boo") }

View File

@@ -1,8 +1,10 @@
package main
import "./foo"
import "github.com/containous/yaegi/_test/foo"
func main() { println(foo.Bar, foo.Boo) }
// Output:
// init boo
// init foo
// BARR Boo

View File

@@ -1,6 +1,6 @@
package main
import "./p1"
import "github.com/containous/yaegi/_test/p1"
func main() { println("num:", p1.Uint32()) }

10
_test/import5.go Normal file
View File

@@ -0,0 +1,10 @@
package main
import boo "github.com/containous/yaegi/_test/foo"
func main() { println(boo.Bar, boo.Boo, boo.Bir) }
// Output:
// init boo
// init foo
// BARR Boo Boo22

View File

@@ -121,8 +121,11 @@ func (interp *Interpreter) gta(root *node, rpath string) ([]*node, error) {
ipath = n.child[0].rval.String()
name = path.Base(ipath)
}
// Try to import a binary package first, or a source package
if interp.binPkg[ipath] != nil {
if name == "." {
switch name {
case "_": // no import of symbols
case ".": // import symbols in current scope
for n, v := range interp.binPkg[ipath] {
typ := v.Type()
if isBinType(v) {
@@ -130,14 +133,22 @@ func (interp *Interpreter) gta(root *node, rpath string) ([]*node, error) {
}
sc.sym[n] = &symbol{kind: binSym, typ: &itype{cat: valueT, rtype: typ}, rval: v}
}
} else {
default: // import symbols in package namespace
sc.sym[name] = &symbol{kind: pkgSym, typ: &itype{cat: binPkgT}, path: ipath}
}
} else {
// TODO: make sure we do not import a src package more than once
err = interp.importSrcFile(rpath, ipath, name)
} else if err = interp.importSrc(rpath, ipath, name); err == nil {
sc.types = interp.universe.types
sc.sym[name] = &symbol{kind: pkgSym, typ: &itype{cat: srcPkgT}, path: ipath}
switch name {
case "_": // no import of symbols
case ".": // import symbols in current namespace
for k, v := range interp.srcPkg[ipath] {
if canExport(k) {
sc.sym[k] = v
}
}
default: // import symbols in package namespace
sc.sym[name] = &symbol{kind: pkgSym, typ: &itype{cat: srcPkgT}, path: ipath}
}
}
case typeSpec:

View File

@@ -54,9 +54,12 @@ type frame struct {
recovered interface{} // to handle panic recover
}
// Exports stores the map of external values per package
// Exports stores the map of binary packages per package path
type Exports map[string]map[string]reflect.Value
// imports stores the map of source packages per package path
type imports map[string]map[string]*symbol
// opt stores interpreter options
type opt struct {
astDot bool // display AST graph (debug)
@@ -74,7 +77,8 @@ type Interpreter struct {
fset *token.FileSet // fileset to locate node in source code
universe *scope // interpreter global level scope
scopes map[string]*scope // package level scopes, indexed by package name
binPkg Exports // runtime binary values used in interpreter
binPkg Exports // binary packages used in interpreter, indexed by path
srcPkg imports // source packages used in interpreter, indexed by path
}
const (
@@ -127,11 +131,12 @@ type Options struct {
func New(options Options) *Interpreter {
i := Interpreter{
opt: opt{context: build.Default},
frame: &frame{data: []reflect.Value{}},
fset: token.NewFileSet(),
universe: initUniverse(),
scopes: map[string]*scope{},
binPkg: Exports{"": map[string]reflect.Value{"_error": reflect.ValueOf((*_error)(nil))}},
frame: &frame{data: []reflect.Value{}},
srcPkg: imports{},
}
i.opt.context.GOPATH = options.GoPath

View File

@@ -1,6 +1,7 @@
package interp_test
import (
"go/build"
"io/ioutil"
"os"
"os/exec"
@@ -35,7 +36,6 @@ func TestInterpConsistencyBuild(t *testing.T) {
file.Name() == "export1.go" || // non-main package
file.Name() == "export0.go" || // non-main package
file.Name() == "io0.go" || // use random number
file.Name() == "import4.go" || // not compatible with go1.13
file.Name() == "op1.go" || // expect error
file.Name() == "bltn0.go" || // expect error
file.Name() == "method16.go" || // private struct field
@@ -79,7 +79,7 @@ func TestInterpConsistencyBuild(t *testing.T) {
r, w, _ := os.Pipe()
os.Stdout = w
i := interp.New(interp.Options{})
i := interp.New(interp.Options{GoPath: build.Default.GOPATH})
i.Name = filePath
i.Use(stdlib.Symbols)
i.Use(interp.Symbols)
@@ -179,7 +179,7 @@ func TestInterpErrorConsistency(t *testing.T) {
t.Fatal(err)
}
i := interp.New(interp.Options{})
i := interp.New(interp.Options{GoPath: build.Default.GOPATH})
i.Name = filePath
i.Use(stdlib.Symbols)

View File

@@ -1,6 +1,7 @@
package interp_test
import (
"go/build"
"go/parser"
"go/token"
"io/ioutil"
@@ -51,7 +52,7 @@ func runCheck(t *testing.T, p string) {
r, w, _ := os.Pipe()
os.Stdout = w
i := interp.New(interp.Options{})
i := interp.New(interp.Options{GoPath: build.Default.GOPATH})
i.Name = p
i.Use(interp.Symbols)
i.Use(stdlib.Symbols)

View File

@@ -8,10 +8,14 @@ import (
"strings"
)
func (interp *Interpreter) importSrcFile(rPath, path, alias string) error {
func (interp *Interpreter) importSrc(rPath, path, alias string) error {
var dir string
var err error
if interp.srcPkg[path] != nil {
return nil
}
// For relative import paths in the form "./xxx" or "../xxx", the initial
// base path is the directory of the interpreter input file, or "." if no file
// was provided.
@@ -92,6 +96,10 @@ func (interp *Interpreter) importSrcFile(rPath, path, alias string) error {
initNodes = append(initNodes, nodes...)
}
// Register source package in the interpreter. The package contains only
// the global symbols in the package scope.
interp.srcPkg[path] = interp.scopes[pkgName].sym
// Rename imported pkgName to alias if they are different
if pkgName != alias {
interp.scopes[alias] = interp.scopes[pkgName]

View File

@@ -383,7 +383,7 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
if v, ok := pkg[name]; ok {
t.cat = valueT
t.rtype = v.Type()
if isBinType(v) {
if isBinType(v) { // a bin type is encoded as a pointer on nil value
t.rtype = t.rtype.Elem()
}
} else {
@@ -391,9 +391,9 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
}
case srcPkgT:
spkg := interp.scopes[pkg]
if st, ok := spkg.sym[name]; ok {
t = st.typ
pkg := interp.srcPkg[sym.path]
if s, ok := pkg[name]; ok {
t = s.typ
} else {
t.incomplete = true
}