diff --git a/pkg/compiler/analysis.go b/pkg/compiler/analysis.go index 4905490c3..febb3a5e3 100644 --- a/pkg/compiler/analysis.go +++ b/pkg/compiler/analysis.go @@ -104,6 +104,32 @@ func lastStmtIsReturn(decl *ast.FuncDecl) (b bool) { return false } +// analyzePkgOrder sets the order in which packages should be processed. +// From Go spec: +// A package with no imports is initialized by assigning initial values to all its package-level variables +// followed by calling all init functions in the order they appear in the source, possibly in multiple files, +// as presented to the compiler. If a package has imports, the imported packages are initialized before +// initializing the package itself. If multiple packages import a package, the imported package +// will be initialized only once. The importing of packages, by construction, guarantees +// that there can be no cyclic initialization dependencies. +func (c *codegen) analyzePkgOrder() { + seen := make(map[string]bool) + info := c.buildInfo.program.Package(c.buildInfo.initialPackage) + c.visitPkg(info.Pkg, seen) +} + +func (c *codegen) visitPkg(pkg *types.Package, seen map[string]bool) { + pkgPath := pkg.Path() + if seen[pkgPath] { + return + } + for _, imp := range pkg.Imports() { + c.visitPkg(imp, seen) + } + seen[pkgPath] = true + c.packages = append(c.packages, pkgPath) +} + func (c *codegen) analyzeFuncUsage() funcUsage { usage := funcUsage{} diff --git a/pkg/compiler/codegen.go b/pkg/compiler/codegen.go index 59bcd9020..b1ae269e4 100644 --- a/pkg/compiler/codegen.go +++ b/pkg/compiler/codegen.go @@ -74,6 +74,9 @@ type codegen struct { // mainPkg is a main package metadata. mainPkg *loader.PackageInfo + // packages contains packages in the order they were loaded. + packages []string + // Label table for recording jump destinations. l []int } @@ -1486,6 +1489,7 @@ func (c *codegen) newLambda(u uint16, lit *ast.FuncLit) { func (c *codegen) compile(info *buildInfo, pkg *loader.PackageInfo) error { c.mainPkg = pkg + c.analyzePkgOrder() funUsage := c.analyzeFuncUsage() // Bring all imported functions into scope. diff --git a/pkg/compiler/compiler.go b/pkg/compiler/compiler.go index 76d48358f..95b952914 100644 --- a/pkg/compiler/compiler.go +++ b/pkg/compiler/compiler.go @@ -49,7 +49,8 @@ type buildInfo struct { // ForEachFile executes fn on each file used in current program. func (c *codegen) ForEachFile(fn func(*ast.File, *types.Package)) { - for _, pkg := range c.buildInfo.program.AllPackages { + for i := range c.packages { + pkg := c.buildInfo.program.Package(c.packages[i]) c.typeInfo = &pkg.Info c.currPkg = pkg.Pkg for _, f := range pkg.Files { diff --git a/pkg/compiler/init_test.go b/pkg/compiler/init_test.go index 878e87ab4..31c8c98b8 100644 --- a/pkg/compiler/init_test.go +++ b/pkg/compiler/init_test.go @@ -60,3 +60,24 @@ func TestInit(t *testing.T) { eval(t, src, big.NewInt(0)) }) } + +func TestImportOrder(t *testing.T) { + t.Run("1,2", func(t *testing.T) { + src := `package foo + import _ "github.com/nspcc-dev/neo-go/pkg/compiler/testdata/pkg1" + import _ "github.com/nspcc-dev/neo-go/pkg/compiler/testdata/pkg2" + import "github.com/nspcc-dev/neo-go/pkg/compiler/testdata/pkg3" + func Main() int { return pkg3.A }` + v := vmAndCompile(t, src) + v.PrintOps() + eval(t, src, big.NewInt(2)) + }) + t.Run("2,1", func(t *testing.T) { + src := `package foo + import _ "github.com/nspcc-dev/neo-go/pkg/compiler/testdata/pkg2" + import _ "github.com/nspcc-dev/neo-go/pkg/compiler/testdata/pkg1" + import "github.com/nspcc-dev/neo-go/pkg/compiler/testdata/pkg3" + func Main() int { return pkg3.A }` + eval(t, src, big.NewInt(1)) + }) +} diff --git a/pkg/compiler/testdata/pkg1/pkg1.go b/pkg/compiler/testdata/pkg1/pkg1.go new file mode 100644 index 000000000..910eb0e45 --- /dev/null +++ b/pkg/compiler/testdata/pkg1/pkg1.go @@ -0,0 +1,7 @@ +package pkg1 + +import "github.com/nspcc-dev/neo-go/pkg/compiler/testdata/pkg3" + +func init() { + pkg3.A = 1 +} diff --git a/pkg/compiler/testdata/pkg2/pkg2.go b/pkg/compiler/testdata/pkg2/pkg2.go new file mode 100644 index 000000000..e95cda011 --- /dev/null +++ b/pkg/compiler/testdata/pkg2/pkg2.go @@ -0,0 +1,9 @@ +package pkg2 + +import ( + "github.com/nspcc-dev/neo-go/pkg/compiler/testdata/pkg3" +) + +func init() { + pkg3.A = 2 +} diff --git a/pkg/compiler/testdata/pkg3/pkg3.go b/pkg/compiler/testdata/pkg3/pkg3.go new file mode 100644 index 000000000..077019e0c --- /dev/null +++ b/pkg/compiler/testdata/pkg3/pkg3.go @@ -0,0 +1,3 @@ +package pkg3 + +var A int