From 7577bbef22e39f73985055eb8af2fc8ecbe13add Mon Sep 17 00:00:00 2001 From: Evgeniy Stratonikov Date: Fri, 26 Feb 2021 18:02:54 +0300 Subject: [PATCH] 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. --- pkg/compiler/inline.go | 3 ++- pkg/compiler/inline_test.go | 15 +++++++++++++++ pkg/compiler/vm_test.go | 14 ++++++++++++++ 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/pkg/compiler/inline.go b/pkg/compiler/inline.go index bc326fb2d..4da30043d 100644 --- a/pkg/compiler/inline.go +++ b/pkg/compiler/inline.go @@ -39,7 +39,8 @@ func (c *codegen) inlineCall(f *funcScope, n *ast.CallExpr) { // while stored in the new. oldScope := c.scope.vars.locals 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() hasVarArgs := !n.Ellipsis.IsValid() diff --git a/pkg/compiler/inline_test.go b/pkg/compiler/inline_test.go index 61b7f1acc..c7dee4599 100644 --- a/pkg/compiler/inline_test.go +++ b/pkg/compiler/inline_test.go @@ -131,6 +131,21 @@ func TestInlineInLoop(t *testing.T) { }` 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) { src := `package foo import "github.com/nspcc-dev/neo-go/pkg/interop/binary" diff --git a/pkg/compiler/vm_test.go b/pkg/compiler/vm_test.go index 0e92a3133..1f414a42b 100644 --- a/pkg/compiler/vm_test.go +++ b/pkg/compiler/vm_test.go @@ -3,6 +3,8 @@ package compiler_test import ( "errors" "fmt" + "math/big" + "strconv" "strings" "testing" @@ -107,6 +109,7 @@ func newStoragePlugin() *storagePlugin { s.interops[interopnames.ToID([]byte(interopnames.SystemStoragePut))] = s.Put s.interops[interopnames.ToID([]byte(interopnames.SystemStorageGetContext))] = s.GetContext 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 return s @@ -123,6 +126,17 @@ func (s *storagePlugin) syscallHandler(v *vm.VM, id uint32) error { 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 { n := v.Estack().Pop().BigInt() base := v.Estack().Pop().BigInt()