2020-08-19 09:36:17 +00:00
|
|
|
package compiler
|
|
|
|
|
|
|
|
import (
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
)
|
|
|
|
|
2024-04-14 09:24:49 +00:00
|
|
|
func testShortenJumps(t *testing.T, before, after []opcode.Opcode, indices []int, spBefore, spAfter map[string][]DebugSeqPoint) {
|
2020-08-19 09:36:17 +00:00
|
|
|
prog := make([]byte, len(before))
|
|
|
|
for i := range before {
|
|
|
|
prog[i] = byte(before[i])
|
|
|
|
}
|
2024-04-14 09:24:49 +00:00
|
|
|
raw := removeNOPs(prog, indices, spBefore)
|
2020-08-19 09:36:17 +00:00
|
|
|
actual := make([]opcode.Opcode, len(raw))
|
|
|
|
for i := range raw {
|
|
|
|
actual[i] = opcode.Opcode(raw[i])
|
|
|
|
}
|
2024-04-14 09:24:49 +00:00
|
|
|
require.Equal(t, spAfter, spBefore)
|
2020-08-19 09:36:17 +00:00
|
|
|
require.Equal(t, after, actual)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestShortenJumps(t *testing.T) {
|
|
|
|
testCases := map[opcode.Opcode]opcode.Opcode{
|
|
|
|
opcode.JMPL: opcode.JMP,
|
|
|
|
opcode.JMPIFL: opcode.JMPIF,
|
|
|
|
opcode.JMPIFNOTL: opcode.JMPIFNOT,
|
|
|
|
opcode.JMPEQL: opcode.JMPEQ,
|
|
|
|
opcode.JMPNEL: opcode.JMPNE,
|
|
|
|
opcode.JMPGTL: opcode.JMPGT,
|
|
|
|
opcode.JMPGEL: opcode.JMPGE,
|
|
|
|
opcode.JMPLEL: opcode.JMPLE,
|
|
|
|
opcode.JMPLTL: opcode.JMPLT,
|
|
|
|
opcode.CALLL: opcode.CALL,
|
|
|
|
}
|
|
|
|
for op, sop := range testCases {
|
|
|
|
t.Run(op.String(), func(t *testing.T) {
|
|
|
|
before := []opcode.Opcode{
|
2022-07-12 10:16:32 +00:00
|
|
|
sop, 6, opcode.NOP, opcode.NOP, opcode.NOP, opcode.PUSH1, opcode.NOP, // <- first jump to here
|
2020-08-19 09:36:17 +00:00
|
|
|
op, 9, 12, 0, 0, opcode.PUSH1, opcode.NOP, // <- last jump to here
|
2022-07-12 10:16:32 +00:00
|
|
|
sop, 249, opcode.NOP, opcode.NOP, opcode.NOP, sop, 0xFF - 5, opcode.NOP, opcode.NOP, opcode.NOP,
|
2020-08-19 09:36:17 +00:00
|
|
|
}
|
|
|
|
after := []opcode.Opcode{
|
|
|
|
sop, 3, opcode.PUSH1, opcode.NOP,
|
|
|
|
op, 3, 12, 0, 0, opcode.PUSH1, opcode.NOP,
|
|
|
|
sop, 249, sop, 0xFF - 2,
|
|
|
|
}
|
2024-04-14 09:24:49 +00:00
|
|
|
spBefore := map[string][]DebugSeqPoint{
|
|
|
|
"test": {
|
|
|
|
DebugSeqPoint{Opcode: 0}, DebugSeqPoint{Opcode: 5},
|
|
|
|
DebugSeqPoint{Opcode: 7}, DebugSeqPoint{Opcode: 12},
|
|
|
|
DebugSeqPoint{Opcode: 14}, DebugSeqPoint{Opcode: 19},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
spAfter := map[string][]DebugSeqPoint{
|
|
|
|
"test": {
|
|
|
|
DebugSeqPoint{Opcode: 0}, DebugSeqPoint{Opcode: 2},
|
|
|
|
DebugSeqPoint{Opcode: 4}, DebugSeqPoint{Opcode: 9},
|
|
|
|
DebugSeqPoint{Opcode: 11}, DebugSeqPoint{Opcode: 13},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
testShortenJumps(t, before, after, []int{2, 3, 4, 16, 17, 18, 21, 22, 23}, spBefore, spAfter)
|
2020-08-19 09:36:17 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
t.Run("NoReplace", func(t *testing.T) {
|
|
|
|
b := []byte{0, 1, 2, 3, 4, 5}
|
|
|
|
expected := []byte{0, 1, 2, 3, 4, 5}
|
2024-04-14 09:24:49 +00:00
|
|
|
require.Equal(t, expected, removeNOPs(b, nil, map[string][]DebugSeqPoint{}))
|
2020-08-19 09:36:17 +00:00
|
|
|
})
|
|
|
|
t.Run("InvalidIndex", func(t *testing.T) {
|
|
|
|
before := []byte{byte(opcode.PUSH1), 0, 0, 0, 0}
|
|
|
|
require.Panics(t, func() {
|
2024-04-14 09:24:49 +00:00
|
|
|
removeNOPs(before, []int{0}, map[string][]DebugSeqPoint{})
|
2020-08-19 09:36:17 +00:00
|
|
|
})
|
|
|
|
})
|
|
|
|
t.Run("SideConditions", func(t *testing.T) {
|
|
|
|
t.Run("Forward", func(t *testing.T) {
|
|
|
|
before := []opcode.Opcode{
|
2022-07-12 10:16:32 +00:00
|
|
|
opcode.JMP, 5, opcode.NOP, opcode.NOP, opcode.NOP,
|
|
|
|
opcode.JMP, 5, opcode.NOP, opcode.NOP, opcode.NOP,
|
2020-08-19 09:36:17 +00:00
|
|
|
}
|
|
|
|
after := []opcode.Opcode{
|
|
|
|
opcode.JMP, 2,
|
|
|
|
opcode.JMP, 2,
|
|
|
|
}
|
2024-04-14 09:24:49 +00:00
|
|
|
spBefore := map[string][]DebugSeqPoint{
|
|
|
|
"test": {
|
|
|
|
DebugSeqPoint{Opcode: 0},
|
|
|
|
DebugSeqPoint{Opcode: 5},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
spAfter := map[string][]DebugSeqPoint{
|
|
|
|
"test": {
|
|
|
|
DebugSeqPoint{Opcode: 0},
|
|
|
|
DebugSeqPoint{Opcode: 2},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
testShortenJumps(t, before, after, []int{2, 3, 4, 7, 8, 9}, spBefore, spAfter)
|
2020-08-19 09:36:17 +00:00
|
|
|
})
|
|
|
|
t.Run("Backwards", func(t *testing.T) {
|
|
|
|
before := []opcode.Opcode{
|
2022-07-12 10:16:32 +00:00
|
|
|
opcode.JMP, 5, opcode.NOP, opcode.NOP, opcode.NOP,
|
|
|
|
opcode.JMP, 0xFF - 4, opcode.NOP, opcode.NOP, opcode.NOP,
|
|
|
|
opcode.JMP, 0xFF - 4, opcode.NOP, opcode.NOP, opcode.NOP,
|
2020-08-19 09:36:17 +00:00
|
|
|
}
|
|
|
|
after := []opcode.Opcode{
|
2022-07-12 10:16:32 +00:00
|
|
|
opcode.JMP, 2,
|
|
|
|
opcode.JMP, 0xFF - 1,
|
2020-08-19 09:36:17 +00:00
|
|
|
opcode.JMP, 0xFF - 1,
|
|
|
|
}
|
2024-04-14 09:24:49 +00:00
|
|
|
spBefore := map[string][]DebugSeqPoint{
|
|
|
|
"test": {
|
|
|
|
DebugSeqPoint{Opcode: 0},
|
|
|
|
DebugSeqPoint{Opcode: 5},
|
|
|
|
DebugSeqPoint{Opcode: 10},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
spAfter := map[string][]DebugSeqPoint{
|
|
|
|
"test": {
|
|
|
|
DebugSeqPoint{Opcode: 0},
|
|
|
|
DebugSeqPoint{Opcode: 2},
|
|
|
|
DebugSeqPoint{Opcode: 4},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
testShortenJumps(t, before, after, []int{2, 3, 4, 7, 8, 9, 12, 13, 14}, spBefore, spAfter)
|
2020-08-19 09:36:17 +00:00
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestWriteJumps(t *testing.T) {
|
|
|
|
c := new(codegen)
|
|
|
|
c.l = []int{10}
|
|
|
|
before := []byte{
|
|
|
|
byte(opcode.NOP), byte(opcode.JMP), 2, byte(opcode.RET),
|
|
|
|
byte(opcode.CALLL), 0, 0, 0, 0, byte(opcode.RET),
|
|
|
|
byte(opcode.PUSH2), byte(opcode.RET),
|
|
|
|
}
|
|
|
|
c.funcs = map[string]*funcScope{
|
|
|
|
"init": {rng: DebugRange{Start: 0, End: 3}},
|
|
|
|
"main": {rng: DebugRange{Start: 4, End: 9}},
|
|
|
|
"method": {rng: DebugRange{Start: 10, End: 11}},
|
|
|
|
}
|
2024-04-14 09:24:49 +00:00
|
|
|
c.sequencePoints = map[string][]DebugSeqPoint{
|
|
|
|
"init": {
|
|
|
|
DebugSeqPoint{Opcode: 1}, DebugSeqPoint{Opcode: 3},
|
|
|
|
},
|
|
|
|
"main": {
|
|
|
|
DebugSeqPoint{Opcode: 4}, DebugSeqPoint{Opcode: 9},
|
|
|
|
},
|
|
|
|
"method": {
|
|
|
|
DebugSeqPoint{Opcode: 10}, DebugSeqPoint{Opcode: 11},
|
|
|
|
},
|
|
|
|
}
|
2020-08-19 09:36:17 +00:00
|
|
|
|
|
|
|
expProg := []byte{
|
|
|
|
byte(opcode.NOP), byte(opcode.JMP), 2, byte(opcode.RET),
|
|
|
|
byte(opcode.CALL), 3, byte(opcode.RET),
|
|
|
|
byte(opcode.PUSH2), byte(opcode.RET),
|
|
|
|
}
|
|
|
|
expFuncs := map[string]*funcScope{
|
|
|
|
"init": {rng: DebugRange{Start: 0, End: 3}},
|
|
|
|
"main": {rng: DebugRange{Start: 4, End: 6}},
|
|
|
|
"method": {rng: DebugRange{Start: 7, End: 8}},
|
|
|
|
}
|
2024-04-14 09:24:49 +00:00
|
|
|
expSeqPoints := map[string][]DebugSeqPoint{
|
|
|
|
"init": {
|
|
|
|
DebugSeqPoint{Opcode: 1}, DebugSeqPoint{Opcode: 3},
|
|
|
|
},
|
|
|
|
"main": {
|
|
|
|
DebugSeqPoint{Opcode: 4}, DebugSeqPoint{Opcode: 6},
|
|
|
|
},
|
|
|
|
"method": {
|
|
|
|
DebugSeqPoint{Opcode: 7}, DebugSeqPoint{Opcode: 8},
|
|
|
|
},
|
|
|
|
}
|
2020-08-19 09:36:17 +00:00
|
|
|
|
|
|
|
buf, err := c.writeJumps(before)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, expProg, buf)
|
|
|
|
require.Equal(t, expFuncs, c.funcs)
|
2024-04-14 09:24:49 +00:00
|
|
|
require.Equal(t, expSeqPoints, c.sequencePoints)
|
2020-08-19 09:36:17 +00:00
|
|
|
}
|
2020-08-19 10:37:24 +00:00
|
|
|
|
|
|
|
func TestWriteJumpsLastJump(t *testing.T) {
|
|
|
|
c := new(codegen)
|
|
|
|
c.l = []int{2}
|
|
|
|
prog := []byte{byte(opcode.JMP), 3, byte(opcode.RET), byte(opcode.JMPL), 0, 0, 0, 0}
|
|
|
|
expected := []byte{byte(opcode.JMP), 3, byte(opcode.RET), byte(opcode.JMP), 0xFF}
|
|
|
|
actual, err := c.writeJumps(prog)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, expected, actual)
|
|
|
|
}
|