compiler: compile init
even if there are no globals
`init` can be useful even if no globals are present, e.g. we can use some syscall inside.
This commit is contained in:
parent
439d9ff94d
commit
4488f61777
3 changed files with 51 additions and 14 deletions
|
@ -36,34 +36,54 @@ func (c *codegen) getIdentName(pkg string, name string) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// traverseGlobals visits and initializes global variables.
|
// traverseGlobals visits and initializes global variables.
|
||||||
// and returns number of variables initialized.
|
// and returns number of variables initialized and
|
||||||
func (c *codegen) traverseGlobals() int {
|
// true if any init functions were encountered.
|
||||||
|
func (c *codegen) traverseGlobals() (int, bool) {
|
||||||
var n int
|
var n int
|
||||||
|
var hasInit bool
|
||||||
c.ForEachFile(func(f *ast.File, _ *types.Package) {
|
c.ForEachFile(func(f *ast.File, _ *types.Package) {
|
||||||
n += countGlobals(f)
|
n += countGlobals(f)
|
||||||
|
if !hasInit {
|
||||||
|
ast.Inspect(f, func(node ast.Node) bool {
|
||||||
|
n, ok := node.(*ast.FuncDecl)
|
||||||
|
if ok {
|
||||||
|
if isInitFunc(n) {
|
||||||
|
hasInit = true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
})
|
})
|
||||||
if n != 0 {
|
}
|
||||||
|
})
|
||||||
|
if n != 0 || hasInit {
|
||||||
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
|
return 0, hasInit
|
||||||
}
|
}
|
||||||
|
if n != 0 {
|
||||||
emit.Instruction(c.prog.BinWriter, opcode.INITSSLOT, []byte{byte(n)})
|
emit.Instruction(c.prog.BinWriter, opcode.INITSSLOT, []byte{byte(n)})
|
||||||
|
}
|
||||||
c.ForEachPackage(func(pkg *loader.PackageInfo) {
|
c.ForEachPackage(func(pkg *loader.PackageInfo) {
|
||||||
|
if n > 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)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if hasInit {
|
||||||
for _, f := range pkg.Files {
|
for _, f := range pkg.Files {
|
||||||
c.fillImportMap(f, pkg.Pkg)
|
c.fillImportMap(f, pkg.Pkg)
|
||||||
c.convertInitFuncs(f, pkg.Pkg)
|
c.convertInitFuncs(f, pkg.Pkg)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// because we reuse `convertFuncDecl` for init funcs,
|
// because we reuse `convertFuncDecl` for init funcs,
|
||||||
// we need to cleare scope, so that global variables
|
// we need to cleare scope, so that global variables
|
||||||
// encountered after will be recognized as globals.
|
// encountered after will be recognized as globals.
|
||||||
c.scope = nil
|
c.scope = nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return n
|
return n, hasInit
|
||||||
}
|
}
|
||||||
|
|
||||||
// countGlobals counts the global variables in the program to add
|
// countGlobals counts the global variables in the program to add
|
||||||
|
|
|
@ -1495,8 +1495,8 @@ func (c *codegen) compile(info *buildInfo, pkg *loader.PackageInfo) error {
|
||||||
// Bring all imported functions into scope.
|
// Bring all imported functions into scope.
|
||||||
c.ForEachFile(c.resolveFuncDecls)
|
c.ForEachFile(c.resolveFuncDecls)
|
||||||
|
|
||||||
n := c.traverseGlobals()
|
n, hasInit := c.traverseGlobals()
|
||||||
if n > 0 {
|
if n > 0 || hasInit {
|
||||||
emit.Opcode(c.prog.BinWriter, opcode.RET)
|
emit.Opcode(c.prog.BinWriter, opcode.RET)
|
||||||
c.initEndOffset = c.prog.Len()
|
c.initEndOffset = c.prog.Len()
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,8 @@ package compiler_test
|
||||||
import (
|
import (
|
||||||
"math/big"
|
"math/big"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestInit(t *testing.T) {
|
func TestInit(t *testing.T) {
|
||||||
|
@ -88,3 +90,18 @@ func TestImportOrder(t *testing.T) {
|
||||||
eval(t, src, big.NewInt(3))
|
eval(t, src, big.NewInt(3))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestInitWithNoGlobals(t *testing.T) {
|
||||||
|
src := `package foo
|
||||||
|
import "github.com/nspcc-dev/neo-go/pkg/interop/runtime"
|
||||||
|
func init() {
|
||||||
|
runtime.Notify("called in '_initialize'")
|
||||||
|
}
|
||||||
|
func Main() int {
|
||||||
|
return 42
|
||||||
|
}`
|
||||||
|
v, s := vmAndCompileInterop(t, src)
|
||||||
|
require.NoError(t, v.Run())
|
||||||
|
assertResult(t, v, big.NewInt(42))
|
||||||
|
require.True(t, len(s.events) == 1)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue