compiler: fix global constant traversal
There can be no global variables, but some global constants.
Introduced in 0b44a430
.
This commit is contained in:
parent
b807fd9e7f
commit
ec58bec803
3 changed files with 37 additions and 14 deletions
|
@ -43,11 +43,13 @@ func (c *codegen) getIdentName(pkg string, name string) string {
|
||||||
// Same for `_deploy()` functions (see docs/compiler.md).
|
// Same for `_deploy()` functions (see docs/compiler.md).
|
||||||
func (c *codegen) traverseGlobals() (int, int, int) {
|
func (c *codegen) traverseGlobals() (int, int, int) {
|
||||||
var hasDefer bool
|
var hasDefer bool
|
||||||
var n int
|
var n, nConst int
|
||||||
initLocals := -1
|
initLocals := -1
|
||||||
deployLocals := -1
|
deployLocals := -1
|
||||||
c.ForEachFile(func(f *ast.File, _ *types.Package) {
|
c.ForEachFile(func(f *ast.File, _ *types.Package) {
|
||||||
n += countGlobals(f)
|
nv, nc := countGlobals(f)
|
||||||
|
n += nv
|
||||||
|
nConst += nc
|
||||||
if initLocals == -1 || deployLocals == -1 || !hasDefer {
|
if initLocals == -1 || deployLocals == -1 || !hasDefer {
|
||||||
ast.Inspect(f, func(node ast.Node) bool {
|
ast.Inspect(f, func(node ast.Node) bool {
|
||||||
switch n := node.(type) {
|
switch n := node.(type) {
|
||||||
|
@ -75,7 +77,7 @@ func (c *codegen) traverseGlobals() (int, int, int) {
|
||||||
if hasDefer {
|
if hasDefer {
|
||||||
n++
|
n++
|
||||||
}
|
}
|
||||||
if n != 0 || initLocals > -1 {
|
if n+nConst != 0 || initLocals > -1 {
|
||||||
if n > 255 {
|
if n > 255 {
|
||||||
c.prog.BinWriter.Err = errors.New("too many global variables")
|
c.prog.BinWriter.Err = errors.New("too many global variables")
|
||||||
return 0, initLocals, deployLocals
|
return 0, initLocals, deployLocals
|
||||||
|
@ -88,7 +90,7 @@ func (c *codegen) traverseGlobals() (int, int, int) {
|
||||||
}
|
}
|
||||||
seenBefore := false
|
seenBefore := false
|
||||||
c.ForEachPackage(func(pkg *loader.PackageInfo) {
|
c.ForEachPackage(func(pkg *loader.PackageInfo) {
|
||||||
if n > 0 {
|
if n+nConst > 0 {
|
||||||
for _, f := range pkg.Files {
|
for _, f := range pkg.Files {
|
||||||
c.fillImportMap(f, pkg.Pkg)
|
c.fillImportMap(f, pkg.Pkg)
|
||||||
c.convertGlobals(f, pkg.Pkg)
|
c.convertGlobals(f, pkg.Pkg)
|
||||||
|
@ -116,7 +118,9 @@ func (c *codegen) traverseGlobals() (int, int, int) {
|
||||||
|
|
||||||
// countGlobals counts the global variables in the program to add
|
// countGlobals counts the global variables in the program to add
|
||||||
// them with the stack size of the function.
|
// them with the stack size of the function.
|
||||||
func countGlobals(f ast.Node) (i int) {
|
// Second returned argument contains amount of global constants.
|
||||||
|
func countGlobals(f ast.Node) (int, int) {
|
||||||
|
var numVar, numConst int
|
||||||
ast.Inspect(f, func(node ast.Node) bool {
|
ast.Inspect(f, func(node ast.Node) bool {
|
||||||
switch n := node.(type) {
|
switch n := node.(type) {
|
||||||
// Skip all function declarations if we have already encountered `defer`.
|
// Skip all function declarations if we have already encountered `defer`.
|
||||||
|
@ -125,11 +129,16 @@ func countGlobals(f ast.Node) (i int) {
|
||||||
// After skipping all funcDecls we are sure that each value spec
|
// After skipping all funcDecls we are sure that each value spec
|
||||||
// is a global declared variable or constant.
|
// is a global declared variable or constant.
|
||||||
case *ast.GenDecl:
|
case *ast.GenDecl:
|
||||||
if n.Tok == token.VAR {
|
isVar := n.Tok == token.VAR
|
||||||
|
if isVar || n.Tok == token.CONST {
|
||||||
for _, s := range n.Specs {
|
for _, s := range n.Specs {
|
||||||
for _, id := range s.(*ast.ValueSpec).Names {
|
for _, id := range s.(*ast.ValueSpec).Names {
|
||||||
if id.Name != "_" {
|
if id.Name != "_" {
|
||||||
i++
|
if isVar {
|
||||||
|
numVar++
|
||||||
|
} else {
|
||||||
|
numConst++
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -138,7 +147,7 @@ func countGlobals(f ast.Node) (i int) {
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
return
|
return numVar, numConst
|
||||||
}
|
}
|
||||||
|
|
||||||
// isExprNil looks if the given expression is a `nil`.
|
// isExprNil looks if the given expression is a `nil`.
|
||||||
|
|
|
@ -202,12 +202,22 @@ func TestExportedVariable(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestExportedConst(t *testing.T) {
|
func TestExportedConst(t *testing.T) {
|
||||||
|
t.Run("with vars", func(t *testing.T) {
|
||||||
src := `package foo
|
src := `package foo
|
||||||
import "github.com/nspcc-dev/neo-go/pkg/compiler/testdata/multi"
|
import "github.com/nspcc-dev/neo-go/pkg/compiler/testdata/multi"
|
||||||
func Main() int {
|
func Main() int {
|
||||||
return multi.SomeConst
|
return multi.SomeConst
|
||||||
}`
|
}`
|
||||||
eval(t, src, big.NewInt(42))
|
eval(t, src, big.NewInt(42))
|
||||||
|
})
|
||||||
|
t.Run("const only", func(t *testing.T) {
|
||||||
|
src := `package foo
|
||||||
|
import "github.com/nspcc-dev/neo-go/pkg/compiler/testdata/constonly"
|
||||||
|
func Main() int {
|
||||||
|
return constonly.Answer
|
||||||
|
}`
|
||||||
|
eval(t, src, big.NewInt(42))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMultipleFuncSameName(t *testing.T) {
|
func TestMultipleFuncSameName(t *testing.T) {
|
||||||
|
|
4
pkg/compiler/testdata/constonly/const.go
vendored
Normal file
4
pkg/compiler/testdata/constonly/const.go
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
package constonly
|
||||||
|
|
||||||
|
// Answer is the only thing you will ever need.
|
||||||
|
const Answer = 42
|
Loading…
Reference in a new issue