compiler: fix global constant traversal

There can be no global variables, but some global constants.
Introduced in 0b44a430.
This commit is contained in:
Evgenii Stratonikov 2020-12-08 15:24:01 +03:00
parent b807fd9e7f
commit ec58bec803
3 changed files with 37 additions and 14 deletions

View file

@ -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`.

View file

@ -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) {

View file

@ -0,0 +1,4 @@
package constonly
// Answer is the only thing you will ever need.
const Answer = 42