diff --git a/pkg/compiler/codegen.go b/pkg/compiler/codegen.go index 3bb330422..4dceef1d1 100644 --- a/pkg/compiler/codegen.go +++ b/pkg/compiler/codegen.go @@ -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) { diff --git a/pkg/compiler/function_call_test.go b/pkg/compiler/function_call_test.go index 2f8d6ea01..ef2fd8c61 100644 --- a/pkg/compiler/function_call_test.go +++ b/pkg/compiler/function_call_test.go @@ -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)) diff --git a/pkg/core/helper_test.go b/pkg/core/helper_test.go index 3fc97d8ea..342678823 100644 --- a/pkg/core/helper_test.go +++ b/pkg/core/helper_test.go @@ -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)