compiler: process packages in deterministic order
This commit is contained in:
parent
b771d2d024
commit
6f2759be3a
7 changed files with 72 additions and 1 deletions
|
@ -104,6 +104,32 @@ func lastStmtIsReturn(decl *ast.FuncDecl) (b bool) {
|
||||||
return false
|
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 {
|
func (c *codegen) analyzeFuncUsage() funcUsage {
|
||||||
usage := funcUsage{}
|
usage := funcUsage{}
|
||||||
|
|
||||||
|
|
|
@ -74,6 +74,9 @@ type codegen struct {
|
||||||
// mainPkg is a main package metadata.
|
// mainPkg is a main package metadata.
|
||||||
mainPkg *loader.PackageInfo
|
mainPkg *loader.PackageInfo
|
||||||
|
|
||||||
|
// packages contains packages in the order they were loaded.
|
||||||
|
packages []string
|
||||||
|
|
||||||
// Label table for recording jump destinations.
|
// Label table for recording jump destinations.
|
||||||
l []int
|
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 {
|
func (c *codegen) compile(info *buildInfo, pkg *loader.PackageInfo) error {
|
||||||
c.mainPkg = pkg
|
c.mainPkg = pkg
|
||||||
|
c.analyzePkgOrder()
|
||||||
funUsage := c.analyzeFuncUsage()
|
funUsage := c.analyzeFuncUsage()
|
||||||
|
|
||||||
// Bring all imported functions into scope.
|
// Bring all imported functions into scope.
|
||||||
|
|
|
@ -49,7 +49,8 @@ type buildInfo struct {
|
||||||
|
|
||||||
// ForEachFile executes fn on each file used in current program.
|
// ForEachFile executes fn on each file used in current program.
|
||||||
func (c *codegen) ForEachFile(fn func(*ast.File, *types.Package)) {
|
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.typeInfo = &pkg.Info
|
||||||
c.currPkg = pkg.Pkg
|
c.currPkg = pkg.Pkg
|
||||||
for _, f := range pkg.Files {
|
for _, f := range pkg.Files {
|
||||||
|
|
|
@ -60,3 +60,24 @@ func TestInit(t *testing.T) {
|
||||||
eval(t, src, big.NewInt(0))
|
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))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
7
pkg/compiler/testdata/pkg1/pkg1.go
vendored
Normal file
7
pkg/compiler/testdata/pkg1/pkg1.go
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
package pkg1
|
||||||
|
|
||||||
|
import "github.com/nspcc-dev/neo-go/pkg/compiler/testdata/pkg3"
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
pkg3.A = 1
|
||||||
|
}
|
9
pkg/compiler/testdata/pkg2/pkg2.go
vendored
Normal file
9
pkg/compiler/testdata/pkg2/pkg2.go
vendored
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
package pkg2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/compiler/testdata/pkg3"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
pkg3.A = 2
|
||||||
|
}
|
3
pkg/compiler/testdata/pkg3/pkg3.go
vendored
Normal file
3
pkg/compiler/testdata/pkg3/pkg3.go
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
package pkg3
|
||||||
|
|
||||||
|
var A int
|
Loading…
Reference in a new issue