compiler: copy locals slice during inline

Consider function call `f(1, g(2, 3))` when
both `f` and `g` are inlined. If `f` contains some locals,
inlining `g` will replace them with it's another locals map,
because slices in Go reuse storage on `append`.
Thus scope needs to be copied.
This commit is contained in:
Evgeniy Stratonikov 2021-02-26 18:02:54 +03:00
parent b66b853285
commit 7577bbef22
3 changed files with 31 additions and 1 deletions

View file

@ -39,7 +39,8 @@ func (c *codegen) inlineCall(f *funcScope, n *ast.CallExpr) {
// while stored in the new. // while stored in the new.
oldScope := c.scope.vars.locals oldScope := c.scope.vars.locals
c.scope.vars.newScope() c.scope.vars.newScope()
newScope := c.scope.vars.locals newScope := make([]map[string]varInfo, len(c.scope.vars.locals))
copy(newScope, c.scope.vars.locals)
defer c.scope.vars.dropScope() defer c.scope.vars.dropScope()
hasVarArgs := !n.Ellipsis.IsValid() hasVarArgs := !n.Ellipsis.IsValid()

View file

@ -131,6 +131,21 @@ func TestInlineInLoop(t *testing.T) {
}` }`
eval(t, src, big.NewInt(20)) eval(t, src, big.NewInt(20))
}) })
t.Run("inlined argument", func(t *testing.T) {
src := `package foo
import "github.com/nspcc-dev/neo-go/pkg/interop/binary"
import "github.com/nspcc-dev/neo-go/pkg/compiler/testdata/inline"
func Main() int {
sum := 0
values := []int{10, 11}
for _, v := range values {
binary.Itoa(v, 10)
sum += inline.VarSum(1, 2, 3, binary.Atoi("4", 10))
}
return sum
}`
eval(t, src, big.NewInt(20))
})
t.Run("check clean stack on return", func(t *testing.T) { t.Run("check clean stack on return", func(t *testing.T) {
src := `package foo src := `package foo
import "github.com/nspcc-dev/neo-go/pkg/interop/binary" import "github.com/nspcc-dev/neo-go/pkg/interop/binary"

View file

@ -3,6 +3,8 @@ package compiler_test
import ( import (
"errors" "errors"
"fmt" "fmt"
"math/big"
"strconv"
"strings" "strings"
"testing" "testing"
@ -107,6 +109,7 @@ func newStoragePlugin() *storagePlugin {
s.interops[interopnames.ToID([]byte(interopnames.SystemStoragePut))] = s.Put s.interops[interopnames.ToID([]byte(interopnames.SystemStoragePut))] = s.Put
s.interops[interopnames.ToID([]byte(interopnames.SystemStorageGetContext))] = s.GetContext s.interops[interopnames.ToID([]byte(interopnames.SystemStorageGetContext))] = s.GetContext
s.interops[interopnames.ToID([]byte(interopnames.SystemRuntimeNotify))] = s.Notify s.interops[interopnames.ToID([]byte(interopnames.SystemRuntimeNotify))] = s.Notify
s.interops[interopnames.ToID([]byte(interopnames.SystemBinaryAtoi))] = s.Atoi
s.interops[interopnames.ToID([]byte(interopnames.SystemBinaryItoa))] = s.Itoa s.interops[interopnames.ToID([]byte(interopnames.SystemBinaryItoa))] = s.Itoa
return s return s
@ -123,6 +126,17 @@ func (s *storagePlugin) syscallHandler(v *vm.VM, id uint32) error {
return errors.New("syscall not found") return errors.New("syscall not found")
} }
func (s *storagePlugin) Atoi(v *vm.VM) error {
str := v.Estack().Pop().String()
base := v.Estack().Pop().BigInt().Int64()
n, err := strconv.ParseInt(str, int(base), 64)
if err != nil {
return err
}
v.Estack().PushVal(big.NewInt(n))
return nil
}
func (s *storagePlugin) Itoa(v *vm.VM) error { func (s *storagePlugin) Itoa(v *vm.VM) error {
n := v.Estack().Pop().BigInt() n := v.Estack().Pop().BigInt()
base := v.Estack().Pop().BigInt() base := v.Estack().Pop().BigInt()