compiler: allow to use local variables in init()

Because body of multiple `init()` functions constitute single
method in contract, we initialize slot with maximum amount of
locals encounterered in any of `init()` functions and clear them
before emitting body of each instance of `init()`.
This commit is contained in:
Evgenii Stratonikov 2020-10-06 18:25:40 +03:00
parent 2d9ef9219a
commit 6701e8cda0
4 changed files with 58 additions and 27 deletions

View file

@ -301,11 +301,23 @@ func isInitFunc(decl *ast.FuncDecl) bool {
decl.Type.Results.NumFields() == 0
}
func (c *codegen) convertInitFuncs(f *ast.File, pkg *types.Package) {
func (c *codegen) clearSlots(n int) {
for i := 0; i < n; i++ {
emit.Opcodes(c.prog.BinWriter, opcode.PUSHNULL)
c.emitStoreByIndex(varLocal, i)
}
}
func (c *codegen) convertInitFuncs(f *ast.File, pkg *types.Package, seenBefore bool) bool {
ast.Inspect(f, func(node ast.Node) bool {
switch n := node.(type) {
case *ast.FuncDecl:
if isInitFunc(n) {
if seenBefore {
cnt, _ := countLocals(n)
c.clearSlots(cnt)
seenBefore = true
}
c.convertFuncDecl(f, n, pkg)
}
case *ast.GenDecl:
@ -313,6 +325,7 @@ func (c *codegen) convertInitFuncs(f *ast.File, pkg *types.Package) {
}
return true
})
return seenBefore
}
func (c *codegen) convertFuncDecl(file ast.Node, decl *ast.FuncDecl, pkg *types.Package) {
@ -345,16 +358,18 @@ func (c *codegen) convertFuncDecl(file ast.Node, decl *ast.FuncDecl, pkg *types.
// All globals copied into the scope of the function need to be added
// to the stack size of the function.
sizeLoc := f.countLocals()
if sizeLoc > 255 {
c.prog.Err = errors.New("maximum of 255 local variables is allowed")
}
sizeArg := f.countArgs()
if sizeArg > 255 {
c.prog.Err = errors.New("maximum of 255 local variables is allowed")
}
if sizeLoc != 0 || sizeArg != 0 {
emit.Instruction(c.prog.BinWriter, opcode.INITSLOT, []byte{byte(sizeLoc), byte(sizeArg)})
if !isInit {
sizeLoc := f.countLocals()
if sizeLoc > 255 {
c.prog.Err = errors.New("maximum of 255 local variables is allowed")
}
sizeArg := f.countArgs()
if sizeArg > 255 {
c.prog.Err = errors.New("maximum of 255 local variables is allowed")
}
if sizeLoc != 0 || sizeArg != 0 {
emit.Instruction(c.prog.BinWriter, opcode.INITSLOT, []byte{byte(sizeLoc), byte(sizeArg)})
}
}
f.vars.newScope()
@ -1777,7 +1792,8 @@ func (c *codegen) compile(info *buildInfo, pkg *loader.PackageInfo) error {
// Bring all imported functions into scope.
c.ForEachFile(c.resolveFuncDecls)
n, hasInit := c.traverseGlobals()
n, initLocals := c.traverseGlobals()
hasInit := initLocals > -1
if n > 0 || hasInit {
emit.Opcodes(c.prog.BinWriter, opcode.RET)
c.initEndOffset = c.prog.Len()