compiler: adjust init/_deploy method offsets during optimization

Because `_initialize` and `_deploy` methods encompass multiple
Go functions, their offsets are stored differently and need
special treatment.
This commit is contained in:
Evgeniy Stratonikov 2021-02-11 15:41:49 +03:00
parent 2ee755e09f
commit 560aff6155
3 changed files with 71 additions and 15 deletions

View file

@ -2011,28 +2011,41 @@ func (c *codegen) writeJumps(b []byte) ([]byte, error) {
}
}
}
if c.deployEndOffset >= 0 {
_, end := correctRange(uint16(c.initEndOffset+1), uint16(c.deployEndOffset), offsets)
c.deployEndOffset = int(end)
}
if c.initEndOffset > 0 {
_, end := correctRange(0, uint16(c.initEndOffset), offsets)
c.initEndOffset = int(end)
}
// Correct function ip range.
// Note: indices are sorted in increasing order.
for _, f := range c.funcs {
start, end := f.rng.Start, f.rng.End
loop:
for _, ind := range offsets {
switch {
case ind > int(f.rng.End):
break loop
case ind < int(f.rng.Start):
start -= longToShortRemoveCount
end -= longToShortRemoveCount
case ind >= int(f.rng.Start):
end -= longToShortRemoveCount
}
}
f.rng.Start = start
f.rng.End = end
f.rng.Start, f.rng.End = correctRange(f.rng.Start, f.rng.End, offsets)
}
return shortenJumps(b, offsets), nil
}
func correctRange(start, end uint16, offsets []int) (uint16, uint16) {
newStart, newEnd := start, end
loop:
for _, ind := range offsets {
switch {
case ind > int(end):
break loop
case ind < int(start):
newStart -= longToShortRemoveCount
newEnd -= longToShortRemoveCount
case ind >= int(start):
newEnd -= longToShortRemoveCount
}
}
return newStart, newEnd
}
func (c *codegen) replaceLabelWithOffset(ip int, arg []byte) (int, error) {
index := binary.LittleEndian.Uint16(arg)
if int(index) > len(c.l) {

View file

@ -286,6 +286,16 @@ func TestVariadicMethod(t *testing.T) {
func TestJumpOptimize(t *testing.T) {
src := `package foo
func init() {
if true {} else {}
var a int
_ = a
}
func _deploy(_ interface{}, upd bool) {
if true {} else {}
t := upd
_ = t
}
func Get1() int { return 1 }
func Get2() int { Get1(); Get1(); Get1(); Get1(); return Get1() }
func Get3() int { return Get2() }
@ -294,6 +304,7 @@ func TestJumpOptimize(t *testing.T) {
}`
b, di, err := compiler.CompileWithDebugInfo("", strings.NewReader(src))
require.NoError(t, err)
require.Equal(t, 6, len(di.Methods))
for _, mi := range di.Methods {
require.Equal(t, b[mi.Range.Start], byte(opcode.INITSLOT))
require.Equal(t, b[mi.Range.End], byte(opcode.RET))

View file

@ -8,11 +8,13 @@ import (
"io/ioutil"
"math/big"
"os"
"strings"
"testing"
"time"
"github.com/nspcc-dev/neo-go/internal/testchain"
"github.com/nspcc-dev/neo-go/internal/testserdes"
"github.com/nspcc-dev/neo-go/pkg/compiler"
"github.com/nspcc-dev/neo-go/pkg/config"
"github.com/nspcc-dev/neo-go/pkg/core/block"
"github.com/nspcc-dev/neo-go/pkg/core/blockchainer"
@ -27,6 +29,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/nef"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm"
@ -148,6 +151,35 @@ func (bc *Blockchain) genBlocks(n int) ([]*block.Block, error) {
return blocks, nil
}
func TestBug1728(t *testing.T) {
src := `package example
import "github.com/nspcc-dev/neo-go/pkg/interop/runtime"
func init() { if true { } else { } }
func _deploy(_ interface{}, isUpdate bool) {
runtime.Log("Deploy")
}`
b, di, err := compiler.CompileWithDebugInfo("foo", strings.NewReader(src))
require.NoError(t, err)
m, err := di.ConvertToManifest(&compiler.Options{Name: "TestContract"})
require.NoError(t, err)
nf, err := nef.NewFile(b)
require.NoError(t, err)
nf.CalculateChecksum()
rawManifest, err := json.Marshal(m)
require.NoError(t, err)
rawNef, err := nf.Bytes()
require.NoError(t, err)
bc := newTestChain(t)
defer bc.Close()
aer, err := invokeContractMethod(bc, 10000000000,
bc.contracts.Management.Hash, "deploy", rawNef, rawManifest)
require.NoError(t, err)
require.Equal(t, aer.VMState, vm.HaltState)
}
func getDecodedBlock(t *testing.T, i int) *block.Block {
data, err := getBlockData(i)
require.NoError(t, err)