compiler: allow to declare global variables in multiple files

Traverse and count globals across all used files.

Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
This commit is contained in:
Evgenii Stratonikov 2020-07-28 10:59:21 +03:00
parent babd84ec10
commit 7009417325
6 changed files with 36 additions and 8 deletions

View file

@ -28,20 +28,18 @@ func (c *codegen) newGlobal(name string) {
// traverseGlobals visits and initializes global variables.
// and returns number of variables initialized.
func (c *codegen) traverseGlobals(fs ...*ast.File) int {
func (c *codegen) traverseGlobals() int {
var n int
for _, f := range fs {
c.ForEachFile(func(f *ast.File) {
n += countGlobals(f)
}
})
if n != 0 {
if n > 255 {
c.prog.BinWriter.Err = errors.New("too many global variables")
return 0
}
emit.Instruction(c.prog.BinWriter, opcode.INITSSLOT, []byte{byte(n)})
for _, f := range fs {
c.convertGlobals(f)
}
c.ForEachFile(c.convertGlobals)
}
return n
}

View file

@ -252,7 +252,7 @@ func (c *codegen) emitDefault(t types.Type) {
// convertGlobals traverses the AST and only converts global declarations.
// If we call this in convertFuncDecl then it will load all global variables
// into the scope of the function.
func (c *codegen) convertGlobals(f ast.Node) {
func (c *codegen) convertGlobals(f *ast.File) {
ast.Inspect(f, func(node ast.Node) bool {
switch n := node.(type) {
case *ast.FuncDecl:
@ -1425,7 +1425,7 @@ func (c *codegen) compile(info *buildInfo, pkg *loader.PackageInfo) error {
}
c.mainPkg = pkg
n := c.traverseGlobals(pkg.Files...)
n := c.traverseGlobals()
if n > 0 {
emit.Opcode(c.prog.BinWriter, opcode.RET)
c.initEndOffset = c.prog.Len()

View file

@ -4,6 +4,7 @@ import (
"bytes"
"encoding/json"
"fmt"
"go/ast"
"go/parser"
"io"
"io/ioutil"
@ -42,6 +43,16 @@ type buildInfo struct {
program *loader.Program
}
// ForEachFile executes fn on each file used in current program.
func (c *codegen) ForEachFile(fn func(*ast.File)) {
for _, pkg := range c.buildInfo.program.AllPackages {
c.typeInfo = &pkg.Info
for _, f := range pkg.Files {
fn(f)
}
}
}
func getBuildInfo(src interface{}) (*buildInfo, error) {
conf := loader.Config{ParserMode: parser.ParseComments}
f, err := conf.ParseFile("", src)

View file

@ -127,3 +127,12 @@ func TestContractWithNoMain(t *testing.T) {
require.Equal(t, 1, v.Estack().Len())
require.Equal(t, big.NewInt(42), v.PopResult())
}
func TestMultipleFiles(t *testing.T) {
src := `package foo
import "github.com/nspcc-dev/neo-go/pkg/compiler/testdata/multi"
func Main() int {
return multi.Sum()
}`
eval(t, src, big.NewInt(42))
}

3
pkg/compiler/testdata/multi/file1.go vendored Normal file
View file

@ -0,0 +1,3 @@
package multi
var SomeVar12 = 12

7
pkg/compiler/testdata/multi/file2.go vendored Normal file
View file

@ -0,0 +1,7 @@
package multi
var SomeVar30 = 30
func Sum() int {
return SomeVar12 + SomeVar30
}