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:
parent
2ee755e09f
commit
560aff6155
3 changed files with 71 additions and 15 deletions
|
@ -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) {
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue