Merge pull request #963 from nspcc-dev/fix/tests

vm: restore JSON tests for NEO3
This commit is contained in:
Roman Khimov 2020-05-22 14:34:08 +03:00 committed by GitHub
commit 5ea51312e6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 436 additions and 335 deletions

View file

@ -26,7 +26,7 @@ var binaryExprTestCases = []testCase{
return x return x
} }
`, `,
[]byte{}, big.NewInt(0),
}, },
{ {
"simple div", "simple div",
@ -97,7 +97,7 @@ var binaryExprTestCases = []testCase{
return 0 return 0
} }
`, `,
[]byte{}, big.NewInt(0),
}, },
{ {
"compare equal strings with eql", "compare equal strings with eql",
@ -139,7 +139,7 @@ var binaryExprTestCases = []testCase{
return 0 return 0
} }
`, `,
[]byte{}, big.NewInt(0),
}, },
{ {
"compare equal ints with eql", "compare equal ints with eql",
@ -167,7 +167,7 @@ var binaryExprTestCases = []testCase{
return 0 return 0
} }
`, `,
[]byte{}, big.NewInt(0),
}, },
{ {
"compare not equal ints with eql", "compare not equal ints with eql",
@ -181,7 +181,7 @@ var binaryExprTestCases = []testCase{
return 0 return 0
} }
`, `,
[]byte{}, big.NewInt(0),
}, },
{ {
"compare not equal ints with neq", "compare not equal ints with neq",

View file

@ -276,7 +276,7 @@ func TestIfUnaryInvert(t *testing.T) {
return 0 return 0
} }
` `
eval(t, src, []byte{}) eval(t, src, big.NewInt(0))
} }
func TestAppendByte(t *testing.T) { func TestAppendByte(t *testing.T) {

View file

@ -39,7 +39,7 @@ func TestNotAssignedFunctionCall(t *testing.T) {
// disable stack checks because it is hard right now // disable stack checks because it is hard right now
// to distinguish between simple function call traversal // to distinguish between simple function call traversal
// and the same traversal inside an assignment. // and the same traversal inside an assignment.
evalWithoutStackChecks(t, src, []byte{}) evalWithoutStackChecks(t, src, big.NewInt(0))
} }
func TestMultipleFunctionCalls(t *testing.T) { func TestMultipleFunctionCalls(t *testing.T) {

View file

@ -30,7 +30,7 @@ func TestGT(t *testing.T) {
return 0 return 0
} }
` `
eval(t, src, []byte{}) eval(t, src, big.NewInt(0))
} }
func TestGTE(t *testing.T) { func TestGTE(t *testing.T) {
@ -44,7 +44,7 @@ func TestGTE(t *testing.T) {
return 0 return 0
} }
` `
eval(t, src, []byte{}) eval(t, src, big.NewInt(0))
} }
func TestLAND(t *testing.T) { func TestLAND(t *testing.T) {
@ -89,5 +89,5 @@ func TestNestedIF(t *testing.T) {
return 0 return 0
} }
` `
eval(t, src, []byte{}) eval(t, src, big.NewInt(0))
} }

View file

@ -32,7 +32,7 @@ func TestImportStruct(t *testing.T) {
return b.Y return b.Y
} }
` `
eval(t, src, []byte{}) eval(t, src, big.NewInt(0))
} }
func TestMultipleDirFileImport(t *testing.T) { func TestMultipleDirFileImport(t *testing.T) {

View file

@ -255,7 +255,7 @@ var structTestCases = []testCase{
return t.y return t.y
} }
`, `,
[]byte{}, big.NewInt(0),
}, },
{ {
"test return struct from func", "test return struct from func",

View file

@ -11,6 +11,8 @@ import (
"math/big" "math/big"
"os" "os"
"path/filepath" "path/filepath"
"regexp"
"strconv"
"strings" "strings"
"testing" "testing"
@ -37,12 +39,12 @@ type (
vmUTExecutionContextState struct { vmUTExecutionContextState struct {
Instruction string `json:"nextInstruction"` Instruction string `json:"nextInstruction"`
InstructionPointer int `json:"instructionPointer"` InstructionPointer int `json:"instructionPointer"`
AStack []vmUTStackItem `json:"altStack"`
EStack []vmUTStackItem `json:"evaluationStack"` EStack []vmUTStackItem `json:"evaluationStack"`
StaticFields []vmUTStackItem `json:"staticFields"`
} }
vmUTExecutionEngineState struct { vmUTExecutionEngineState struct {
State vmUTState `json:"state"` State State `json:"state"`
ResultStack []vmUTStackItem `json:"resultStack"` ResultStack []vmUTStackItem `json:"resultStack"`
InvocationStack []vmUTExecutionContextState `json:"invocationStack"` InvocationStack []vmUTExecutionContextState `json:"invocationStack"`
} }
@ -59,8 +61,6 @@ type (
Result vmUTExecutionEngineState `json:"result"` Result vmUTExecutionEngineState `json:"result"`
} }
vmUTState State
vmUTStackItemType string vmUTStackItemType string
) )
@ -73,25 +73,27 @@ type stackItemAUX struct {
} }
const ( const (
vmExecute vmUTActionType = "Execute" vmExecute vmUTActionType = "execute"
vmStepInto vmUTActionType = "StepInto" vmStepInto vmUTActionType = "stepinto"
vmStepOut vmUTActionType = "StepOut" vmStepOut vmUTActionType = "stepout"
vmStepOver vmUTActionType = "StepOver" vmStepOver vmUTActionType = "stepover"
typeArray vmUTStackItemType = "Array" typeArray vmUTStackItemType = "array"
typeBoolean vmUTStackItemType = "Boolean" typeBoolean vmUTStackItemType = "boolean"
typeByteArray vmUTStackItemType = "ByteArray" typeBuffer vmUTStackItemType = "buffer"
typeInteger vmUTStackItemType = "Integer" typeByteString vmUTStackItemType = "bytestring"
typeInterop vmUTStackItemType = "Interop" typeInteger vmUTStackItemType = "integer"
typeMap vmUTStackItemType = "Map" typeInterop vmUTStackItemType = "interop"
typeString vmUTStackItemType = "String" typeMap vmUTStackItemType = "map"
typeStruct vmUTStackItemType = "Struct" typeNull vmUTStackItemType = "null"
typePointer vmUTStackItemType = "pointer"
typeString vmUTStackItemType = "string"
typeStruct vmUTStackItemType = "struct"
testsDir = "testdata/neo-vm/tests/neo-vm.Tests/Tests/" testsDir = "testdata/neo-vm/tests/neo-vm.Tests/Tests/"
) )
func TestUT(t *testing.T) { func TestUT(t *testing.T) {
t.Skip()
testsRan := false testsRan := false
err := filepath.Walk(testsDir, func(path string, info os.FileInfo, err error) error { err := filepath.Walk(testsDir, func(path string, info os.FileInfo, err error) error {
if !strings.HasSuffix(path, ".json") { if !strings.HasSuffix(path, ".json") {
@ -110,7 +112,7 @@ func TestUT(t *testing.T) {
func getTestingInterop(id uint32) *InteropFuncPrice { func getTestingInterop(id uint32) *InteropFuncPrice {
if id == binary.LittleEndian.Uint32([]byte{0x77, 0x77, 0x77, 0x77}) { if id == binary.LittleEndian.Uint32([]byte{0x77, 0x77, 0x77, 0x77}) {
return &InteropFuncPrice{InteropFunc(func(v *VM) error { return &InteropFuncPrice{InteropFunc(func(v *VM) error {
v.estack.Push(&Element{value: (*InteropItem)(nil)}) v.estack.PushVal(&InteropItem{new(int)})
return nil return nil
}), 0} }), 0}
} }
@ -121,12 +123,24 @@ func testFile(t *testing.T, filename string) {
data, err := ioutil.ReadFile(filename) data, err := ioutil.ReadFile(filename)
require.NoError(t, err) require.NoError(t, err)
// get rid of possible BOM
if len(data) > 2 && data[0] == 0xef && data[1] == 0xbb && data[2] == 0xbf {
data = data[3:]
}
if strings.HasSuffix(filename, "MEMCPY.json") {
return // FIXME not a valid JSON https://github.com/neo-project/neo-vm/issues/322
}
ut := new(vmUT) ut := new(vmUT)
require.NoError(t, json.Unmarshal(data, ut)) require.NoErrorf(t, json.Unmarshal(data, ut), "file: %s", filename)
t.Run(ut.Category+":"+ut.Name, func(t *testing.T) { t.Run(ut.Category+":"+ut.Name, func(t *testing.T) {
isRot := strings.HasSuffix(filename, "ROT.json")
for i := range ut.Tests { for i := range ut.Tests {
test := ut.Tests[i] test := ut.Tests[i]
if isRot && test.Name == "Without push" {
return // FIXME #927 single ROT is interpreted as PUSH1
}
t.Run(ut.Tests[i].Name, func(t *testing.T) { t.Run(ut.Tests[i].Name, func(t *testing.T) {
prog := []byte(test.Script) prog := []byte(test.Script)
vm := load(prog) vm := load(prog)
@ -136,8 +150,8 @@ func testFile(t *testing.T, filename string) {
for i := range test.Steps { for i := range test.Steps {
execStep(t, vm, test.Steps[i]) execStep(t, vm, test.Steps[i])
result := test.Steps[i].Result result := test.Steps[i].Result
require.Equal(t, State(result.State), vm.state) require.Equal(t, result.State, vm.state)
if result.State == vmUTState(faultState) { // do not compare stacks on fault if result.State == faultState { // do not compare stacks on fault
continue continue
} }
@ -146,10 +160,12 @@ func testFile(t *testing.T, filename string) {
ctx := vm.istack.Peek(i).Value().(*Context) ctx := vm.istack.Peek(i).Value().(*Context)
if ctx.nextip < len(ctx.prog) { if ctx.nextip < len(ctx.prog) {
require.Equal(t, s.InstructionPointer, ctx.nextip) require.Equal(t, s.InstructionPointer, ctx.nextip)
require.Equal(t, s.Instruction, opcode.Opcode(ctx.prog[ctx.nextip]).String()) op, err := opcode.FromString(s.Instruction)
require.NoError(t, err)
require.Equal(t, op, opcode.Opcode(ctx.prog[ctx.nextip]))
} }
compareStacks(t, s.EStack, vm.estack) compareStacks(t, s.EStack, vm.estack)
compareStacks(t, s.AStack, vm.astack) compareSlots(t, s.StaticFields, vm.static)
} }
} }
@ -180,31 +196,47 @@ func compareItems(t *testing.T, a, b StackItem) {
default: default:
require.Fail(t, "wrong type") require.Fail(t, "wrong type")
} }
case *PointerItem:
p, ok := b.(*PointerItem)
require.True(t, ok)
require.Equal(t, si.pos, p.pos) // there no script in test files
default: default:
require.Equal(t, a, b) require.Equal(t, a, b)
} }
} }
func compareStacks(t *testing.T, expected []vmUTStackItem, actual *Stack) { func compareStacks(t *testing.T, expected []vmUTStackItem, actual *Stack) {
compareItemArrays(t, expected, actual.Len(), func(i int) StackItem { return actual.Peek(i).Item() })
}
func compareSlots(t *testing.T, expected []vmUTStackItem, actual *Slot) {
if actual == nil && len(expected) == 0 {
return
}
require.NotNil(t, actual)
compareItemArrays(t, expected, actual.Size(), actual.Get)
}
func compareItemArrays(t *testing.T, expected []vmUTStackItem, n int, getItem func(i int) StackItem) {
if expected == nil { if expected == nil {
return return
} }
require.Equal(t, len(expected), actual.Len()) require.Equal(t, len(expected), n)
for i, item := range expected { for i, item := range expected {
e := actual.Peek(i) it := getItem(i)
require.NotNil(t, e) require.NotNil(t, it)
if item.Type == typeInterop { if item.Type == typeInterop {
require.IsType(t, (*InteropItem)(nil), e.value) require.IsType(t, (*InteropItem)(nil), it)
continue continue
} }
compareItems(t, item.toStackItem(), e.value) compareItems(t, item.toStackItem(), it)
} }
} }
func (v *vmUTStackItem) toStackItem() StackItem { func (v *vmUTStackItem) toStackItem() StackItem {
switch v.Type { switch v.Type.toLower() {
case typeArray: case typeArray:
items := v.Value.([]vmUTStackItem) items := v.Value.([]vmUTStackItem)
result := make([]StackItem, len(items)) result := make([]StackItem, len(items))
@ -217,20 +249,19 @@ func (v *vmUTStackItem) toStackItem() StackItem {
case typeString: case typeString:
panic("not implemented") panic("not implemented")
case typeMap: case typeMap:
items := v.Value.(map[string]vmUTStackItem) return v.Value.(*MapItem)
result := NewMapItem()
for k, v := range items {
var item vmUTStackItem
_ = json.Unmarshal([]byte(`"`+k+`"`), &item)
result.Add(item.toStackItem(), v.toStackItem())
}
return result
case typeInterop: case typeInterop:
panic("not implemented") panic("not implemented")
case typeByteArray: case typeByteString:
return &ByteArrayItem{ return &ByteArrayItem{
v.Value.([]byte), v.Value.([]byte),
} }
case typeBuffer:
return &BufferItem{v.Value.([]byte)}
case typePointer:
return NewPointerItem(v.Value.(int), nil)
case typeNull:
return NullItem{}
case typeBoolean: case typeBoolean:
return &BoolItem{ return &BoolItem{
v.Value.(bool), v.Value.(bool),
@ -249,14 +280,14 @@ func (v *vmUTStackItem) toStackItem() StackItem {
value: result, value: result,
} }
default: default:
panic("invalid type") panic(fmt.Sprintf("invalid type: %s", v.Type))
} }
} }
func execStep(t *testing.T, v *VM, step vmUTStep) { func execStep(t *testing.T, v *VM, step vmUTStep) {
for i, a := range step.Actions { for i, a := range step.Actions {
var err error var err error
switch a { switch a.toLower() {
case vmExecute: case vmExecute:
err = v.Run() err = v.Run()
case vmStepInto: case vmStepInto:
@ -276,30 +307,65 @@ func execStep(t *testing.T, v *VM, step vmUTStep) {
} }
} }
func (v *vmUTState) UnmarshalJSON(data []byte) error { func jsonStringToInteger(s string) StackItem {
switch s := string(data); s { b, err := decodeHex(s)
case `"Break"`: if err == nil {
*v = vmUTState(breakState) return NewBigIntegerItem(new(big.Int).SetBytes(b))
case `"Fault"`:
*v = vmUTState(faultState)
case `"Halt"`:
*v = vmUTState(haltState)
default:
panic(fmt.Sprintf("invalid state: %s", s))
} }
return nil return nil
} }
func (v vmUTStackItemType) toLower() vmUTStackItemType {
return vmUTStackItemType(strings.ToLower(string(v)))
}
func (v *vmUTScript) UnmarshalJSON(data []byte) error { func (v *vmUTScript) UnmarshalJSON(data []byte) error {
b, err := decodeBytes(data) var ops []string
if err != nil { if err := json.Unmarshal(data, &ops); err != nil {
return err return err
} }
*v = vmUTScript(b) var script []byte
for i := range ops {
if b, ok := decodeSingle(ops[i]); ok {
script = append(script, b...)
} else {
const regex = `(?P<hex>(?:0x)?[0-9a-zA-Z]+)\*(?P<num>[0-9]+)`
re := regexp.MustCompile(regex)
ss := re.FindStringSubmatch(ops[i])
if len(ss) != 3 {
return fmt.Errorf("invalid script part: %s", ops[i])
}
b, ok := decodeSingle(ss[1])
if !ok {
return fmt.Errorf("invalid script part: %s", ops[i])
}
num, err := strconv.Atoi(ss[2])
if err != nil {
return fmt.Errorf("invalid script part: %s", ops[i])
}
for i := 0; i < num; i++ {
script = append(script, b...)
}
}
}
*v = script
return nil return nil
} }
func decodeSingle(s string) ([]byte, bool) {
if op, err := opcode.FromString(s); err == nil {
return []byte{byte(op)}, true
}
b, err := decodeHex(s)
return b, err == nil
}
func (v vmUTActionType) toLower() vmUTActionType {
return vmUTActionType(strings.ToLower(string(v)))
}
func (v *vmUTActionType) UnmarshalJSON(data []byte) error { func (v *vmUTActionType) UnmarshalJSON(data []byte) error {
return json.Unmarshal(data, (*string)(v)) return json.Unmarshal(data, (*string)(v))
} }
@ -312,14 +378,14 @@ func (v *vmUTStackItem) UnmarshalJSON(data []byte) error {
v.Type = si.Type v.Type = si.Type
switch si.Type { switch typ := si.Type.toLower(); typ {
case typeArray, typeStruct: case typeArray, typeStruct:
var a []vmUTStackItem var a []vmUTStackItem
if err := json.Unmarshal(si.Value, &a); err != nil { if err := json.Unmarshal(si.Value, &a); err != nil {
return err return err
} }
v.Value = a v.Value = a
case typeInteger: case typeInteger, typePointer:
num := new(big.Int) num := new(big.Int)
var a int64 var a int64
var s string var s string
@ -330,27 +396,57 @@ func (v *vmUTStackItem) UnmarshalJSON(data []byte) error {
} else { } else {
panic(fmt.Sprintf("invalid integer: %v", si.Value)) panic(fmt.Sprintf("invalid integer: %v", si.Value))
} }
v.Value = num if typ == typePointer {
v.Value = int(num.Int64())
} else {
v.Value = num
}
case typeBoolean: case typeBoolean:
var b bool var b bool
if err := json.Unmarshal(si.Value, &b); err != nil { if err := json.Unmarshal(si.Value, &b); err != nil {
return err return err
} }
v.Value = b v.Value = b
case typeByteArray: case typeByteString, typeBuffer:
b, err := decodeBytes(si.Value) b, err := decodeBytes(si.Value)
if err != nil { if err != nil {
return err return err
} }
v.Value = b v.Value = b
case typeInterop: case typeInterop, typeNull:
v.Value = nil v.Value = nil
case typeMap: case typeMap:
var m map[string]vmUTStackItem // we want to have the same order as in test file, so a custom decoder is used
if err := json.Unmarshal(si.Value, &m); err != nil { d := json.NewDecoder(bytes.NewReader(si.Value))
return err if tok, err := d.Token(); err != nil || tok != json.Delim('{') {
return fmt.Errorf("invalid map start")
} }
v.Value = m
result := NewMapItem()
for {
tok, err := d.Token()
if err != nil {
return err
} else if tok == json.Delim('}') {
break
}
key, ok := tok.(string)
if !ok {
return fmt.Errorf("string expected in map key")
}
var it vmUTStackItem
if err := d.Decode(&it); err != nil {
return fmt.Errorf("can't decode map value: %v", err)
}
item := jsonStringToInteger(key)
if item == nil {
return fmt.Errorf("can't unmarshal StackItem %s", key)
}
result.Add(item, it.toStackItem())
}
v.Value = result
case typeString: case typeString:
panic("not implemented") panic("not implemented")
default: default:
@ -366,12 +462,18 @@ func decodeBytes(data []byte) ([]byte, error) {
return []byte{}, nil return []byte{}, nil
} }
hdata := data[3 : len(data)-1] data = data[1 : len(data)-1] // strip quotes
if b, err := hex.DecodeString(string(hdata)); err == nil { if b, err := decodeHex(string(data)); err == nil {
return b, nil return b, nil
} }
data = data[1 : len(data)-1]
r := base64.NewDecoder(base64.StdEncoding, bytes.NewReader(data)) r := base64.NewDecoder(base64.StdEncoding, bytes.NewReader(data))
return ioutil.ReadAll(r) return ioutil.ReadAll(r)
} }
func decodeHex(s string) ([]byte, error) {
if strings.HasPrefix(s, "0x") {
s = s[2:]
}
return hex.DecodeString(s)
}

View file

@ -0,0 +1,20 @@
package opcode
import "errors"
var stringToOpcode = make(map[string]Opcode)
func init() {
for i := 0; i < 255; i++ {
op := Opcode(i)
stringToOpcode[op.String()] = op
}
}
// FromString converts string representation to and opcode itself.
func FromString(s string) (Opcode, error) {
if op, ok := stringToOpcode[s]; ok {
return op, nil
}
return 0, errors.New("invalid opcode")
}

View file

@ -46,25 +46,25 @@ const (
// Flow control // Flow control
NOP Opcode = 0x21 NOP Opcode = 0x21
JMP Opcode = 0x22 JMP Opcode = 0x22
JMPL Opcode = 0x23 JMPL Opcode = 0x23 // JMP_L
JMPIF Opcode = 0x24 JMPIF Opcode = 0x24
JMPIFL Opcode = 0x25 JMPIFL Opcode = 0x25 // JMPIF_L
JMPIFNOT Opcode = 0x26 JMPIFNOT Opcode = 0x26
JMPIFNOTL Opcode = 0x27 JMPIFNOTL Opcode = 0x27 // JMPIFNOT_L
JMPEQ Opcode = 0x28 JMPEQ Opcode = 0x28
JMPEQL Opcode = 0x29 JMPEQL Opcode = 0x29 // JMPEQ_L
JMPNE Opcode = 0x2A JMPNE Opcode = 0x2A
JMPNEL Opcode = 0x2B JMPNEL Opcode = 0x2B // JMPNE_L
JMPGT Opcode = 0x2C JMPGT Opcode = 0x2C
JMPGTL Opcode = 0x2D JMPGTL Opcode = 0x2D // JMPGT_L
JMPGE Opcode = 0x2E JMPGE Opcode = 0x2E
JMPGEL Opcode = 0x2F JMPGEL Opcode = 0x2F // JMPGE_L
JMPLT Opcode = 0x30 JMPLT Opcode = 0x30
JMPLTL Opcode = 0x31 JMPLTL Opcode = 0x31 // JMPLT_L
JMPLE Opcode = 0x32 JMPLE Opcode = 0x32
JMPLEL Opcode = 0x33 JMPLEL Opcode = 0x33 // JMPLE_L
CALL Opcode = 0x34 CALL Opcode = 0x34
CALLL Opcode = 0x35 CALLL Opcode = 0x35 // CALL_L
CALLA Opcode = 0x36 CALLA Opcode = 0x36
// Exceptions // Exceptions
@ -86,8 +86,8 @@ const (
PICK Opcode = 0x4D PICK Opcode = 0x4D
TUCK Opcode = 0x4E TUCK Opcode = 0x4E
SWAP Opcode = 0x50 SWAP Opcode = 0x50
OLDPUSH1 Opcode = 0x51 // FIXME remove #927
ROT Opcode = 0x51 ROT Opcode = 0x51
OLDPUSH1 Opcode = 0x51 // FIXME remove #927
ROLL Opcode = 0x52 ROLL Opcode = 0x52
REVERSE3 Opcode = 0x53 REVERSE3 Opcode = 0x53
REVERSE4 Opcode = 0x54 REVERSE4 Opcode = 0x54
@ -193,7 +193,7 @@ const (
UNPACK Opcode = 0xC1 UNPACK Opcode = 0xC1
NEWARRAY0 Opcode = 0xC2 NEWARRAY0 Opcode = 0xC2
NEWARRAY Opcode = 0xC3 NEWARRAY Opcode = 0xC3
NEWARRAYT Opcode = 0xC4 NEWARRAYT Opcode = 0xC4 // NEWARRAY_T
NEWSTRUCT0 Opcode = 0xC5 NEWSTRUCT0 Opcode = 0xC5
NEWSTRUCT Opcode = 0xC6 NEWSTRUCT Opcode = 0xC6
NEWMAP Opcode = 0xC8 NEWMAP Opcode = 0xC8

View file

@ -1,4 +1,4 @@
// Code generated by "stringer -type Opcode"; DO NOT EDIT. // Code generated by "stringer -type Opcode -linecomment"; DO NOT EDIT.
package opcode package opcode
@ -76,8 +76,8 @@ func _() {
_ = x[PICK-77] _ = x[PICK-77]
_ = x[TUCK-78] _ = x[TUCK-78]
_ = x[SWAP-80] _ = x[SWAP-80]
_ = x[OLDPUSH1-81]
_ = x[ROT-81] _ = x[ROT-81]
_ = x[OLDPUSH1-81]
_ = x[ROLL-82] _ = x[ROLL-82]
_ = x[REVERSE3-83] _ = x[REVERSE3-83]
_ = x[REVERSE4-84] _ = x[REVERSE4-84]
@ -192,7 +192,7 @@ func _() {
_ = x[CONVERT-219] _ = x[CONVERT-219]
} }
const _Opcode_name = "PUSHINT8PUSHINT16PUSHINT32PUSHINT64PUSHINT128PUSHINT256PUSHAPUSHNULLPUSHDATA1PUSHDATA2PUSHDATA4PUSHM1PUSH0PUSH1PUSH2PUSH3PUSH4PUSH5PUSH6PUSH7PUSH8PUSH9PUSH10PUSH11PUSH12PUSH13PUSH14PUSH15PUSH16NOPJMPJMPLJMPIFJMPIFLJMPIFNOTJMPIFNOTLJMPEQJMPEQLJMPNEJMPNELJMPGTJMPGTLJMPGEJMPGELJMPLTJMPLTLJMPLEJMPLELCALLCALLLCALLAABORTASSERTTHROWRETSYSCALLDEPTHDROPNIPXDROPCLEARDUPOVERPICKTUCKSWAPOLDPUSH1ROLLREVERSE3REVERSE4REVERSENINITSSLOTINITSLOTLDSFLD0LDSFLD1LDSFLD2LDSFLD3LDSFLD4LDSFLD5LDSFLD6LDSFLDSTSFLD0STSFLD1STSFLD2STSFLD3STSFLD4STSFLD5STSFLD6STSFLDLDLOC0LDLOC1LDLOC2LDLOC3LDLOC4LDLOC5LDLOC6LDLOCSTLOC0STLOC1STLOC2STLOC3STLOC4STLOC5STLOC6STLOCLDARG0LDARG1LDARG2LDARG3LDARG4LDARG5LDARG6LDARGSTARG0STARG1STARG2STARG3STARG4STARG5STARG6STARGNEWBUFFERMEMCPYCATSUBSTRLEFTRIGHTINVERTANDORXOREQUALNOTEQUALSIGNABSNEGATEINCDECADDSUBMULDIVMODSHLSHRNOTBOOLANDBOOLORNZNUMEQUALNUMNOTEQUALLTLTEGTGTEMINMAXWITHINPACKUNPACKNEWARRAY0NEWARRAYNEWARRAYTNEWSTRUCT0NEWSTRUCTNEWMAPSIZEHASKEYKEYSVALUESPICKITEMAPPENDSETITEMREVERSEITEMSREMOVECLEARITEMSISNULLISTYPECONVERT" const _Opcode_name = "PUSHINT8PUSHINT16PUSHINT32PUSHINT64PUSHINT128PUSHINT256PUSHAPUSHNULLPUSHDATA1PUSHDATA2PUSHDATA4PUSHM1PUSH0PUSH1PUSH2PUSH3PUSH4PUSH5PUSH6PUSH7PUSH8PUSH9PUSH10PUSH11PUSH12PUSH13PUSH14PUSH15PUSH16NOPJMPJMP_LJMPIFJMPIF_LJMPIFNOTJMPIFNOT_LJMPEQJMPEQ_LJMPNEJMPNE_LJMPGTJMPGT_LJMPGEJMPGE_LJMPLTJMPLT_LJMPLEJMPLE_LCALLCALL_LCALLAABORTASSERTTHROWRETSYSCALLDEPTHDROPNIPXDROPCLEARDUPOVERPICKTUCKSWAPROTROLLREVERSE3REVERSE4REVERSENINITSSLOTINITSLOTLDSFLD0LDSFLD1LDSFLD2LDSFLD3LDSFLD4LDSFLD5LDSFLD6LDSFLDSTSFLD0STSFLD1STSFLD2STSFLD3STSFLD4STSFLD5STSFLD6STSFLDLDLOC0LDLOC1LDLOC2LDLOC3LDLOC4LDLOC5LDLOC6LDLOCSTLOC0STLOC1STLOC2STLOC3STLOC4STLOC5STLOC6STLOCLDARG0LDARG1LDARG2LDARG3LDARG4LDARG5LDARG6LDARGSTARG0STARG1STARG2STARG3STARG4STARG5STARG6STARGNEWBUFFERMEMCPYCATSUBSTRLEFTRIGHTINVERTANDORXOREQUALNOTEQUALSIGNABSNEGATEINCDECADDSUBMULDIVMODSHLSHRNOTBOOLANDBOOLORNZNUMEQUALNUMNOTEQUALLTLTEGTGTEMINMAXWITHINPACKUNPACKNEWARRAY0NEWARRAYNEWARRAY_TNEWSTRUCT0NEWSTRUCTNEWMAPSIZEHASKEYKEYSVALUESPICKITEMAPPENDSETITEMREVERSEITEMSREMOVECLEARITEMSISNULLISTYPECONVERT"
var _Opcode_map = map[Opcode]string{ var _Opcode_map = map[Opcode]string{
0: _Opcode_name[0:8], 0: _Opcode_name[0:8],
@ -226,154 +226,154 @@ var _Opcode_map = map[Opcode]string{
32: _Opcode_name[187:193], 32: _Opcode_name[187:193],
33: _Opcode_name[193:196], 33: _Opcode_name[193:196],
34: _Opcode_name[196:199], 34: _Opcode_name[196:199],
35: _Opcode_name[199:203], 35: _Opcode_name[199:204],
36: _Opcode_name[203:208], 36: _Opcode_name[204:209],
37: _Opcode_name[208:214], 37: _Opcode_name[209:216],
38: _Opcode_name[214:222], 38: _Opcode_name[216:224],
39: _Opcode_name[222:231], 39: _Opcode_name[224:234],
40: _Opcode_name[231:236], 40: _Opcode_name[234:239],
41: _Opcode_name[236:242], 41: _Opcode_name[239:246],
42: _Opcode_name[242:247], 42: _Opcode_name[246:251],
43: _Opcode_name[247:253], 43: _Opcode_name[251:258],
44: _Opcode_name[253:258], 44: _Opcode_name[258:263],
45: _Opcode_name[258:264], 45: _Opcode_name[263:270],
46: _Opcode_name[264:269], 46: _Opcode_name[270:275],
47: _Opcode_name[269:275], 47: _Opcode_name[275:282],
48: _Opcode_name[275:280], 48: _Opcode_name[282:287],
49: _Opcode_name[280:286], 49: _Opcode_name[287:294],
50: _Opcode_name[286:291], 50: _Opcode_name[294:299],
51: _Opcode_name[291:297], 51: _Opcode_name[299:306],
52: _Opcode_name[297:301], 52: _Opcode_name[306:310],
53: _Opcode_name[301:306], 53: _Opcode_name[310:316],
54: _Opcode_name[306:311], 54: _Opcode_name[316:321],
55: _Opcode_name[311:316], 55: _Opcode_name[321:326],
56: _Opcode_name[316:322], 56: _Opcode_name[326:332],
58: _Opcode_name[322:327], 58: _Opcode_name[332:337],
64: _Opcode_name[327:330], 64: _Opcode_name[337:340],
65: _Opcode_name[330:337], 65: _Opcode_name[340:347],
67: _Opcode_name[337:342], 67: _Opcode_name[347:352],
69: _Opcode_name[342:346], 69: _Opcode_name[352:356],
70: _Opcode_name[346:349], 70: _Opcode_name[356:359],
72: _Opcode_name[349:354], 72: _Opcode_name[359:364],
73: _Opcode_name[354:359], 73: _Opcode_name[364:369],
74: _Opcode_name[359:362], 74: _Opcode_name[369:372],
75: _Opcode_name[362:366], 75: _Opcode_name[372:376],
77: _Opcode_name[366:370], 77: _Opcode_name[376:380],
78: _Opcode_name[370:374], 78: _Opcode_name[380:384],
80: _Opcode_name[374:378], 80: _Opcode_name[384:388],
81: _Opcode_name[378:386], 81: _Opcode_name[388:391],
82: _Opcode_name[386:390], 82: _Opcode_name[391:395],
83: _Opcode_name[390:398], 83: _Opcode_name[395:403],
84: _Opcode_name[398:406], 84: _Opcode_name[403:411],
85: _Opcode_name[406:414], 85: _Opcode_name[411:419],
86: _Opcode_name[414:423], 86: _Opcode_name[419:428],
87: _Opcode_name[423:431], 87: _Opcode_name[428:436],
88: _Opcode_name[431:438], 88: _Opcode_name[436:443],
89: _Opcode_name[438:445], 89: _Opcode_name[443:450],
90: _Opcode_name[445:452], 90: _Opcode_name[450:457],
91: _Opcode_name[452:459], 91: _Opcode_name[457:464],
92: _Opcode_name[459:466], 92: _Opcode_name[464:471],
93: _Opcode_name[466:473], 93: _Opcode_name[471:478],
94: _Opcode_name[473:480], 94: _Opcode_name[478:485],
95: _Opcode_name[480:486], 95: _Opcode_name[485:491],
96: _Opcode_name[486:493], 96: _Opcode_name[491:498],
97: _Opcode_name[493:500], 97: _Opcode_name[498:505],
98: _Opcode_name[500:507], 98: _Opcode_name[505:512],
99: _Opcode_name[507:514], 99: _Opcode_name[512:519],
100: _Opcode_name[514:521], 100: _Opcode_name[519:526],
101: _Opcode_name[521:528], 101: _Opcode_name[526:533],
102: _Opcode_name[528:535], 102: _Opcode_name[533:540],
103: _Opcode_name[535:541], 103: _Opcode_name[540:546],
104: _Opcode_name[541:547], 104: _Opcode_name[546:552],
105: _Opcode_name[547:553], 105: _Opcode_name[552:558],
106: _Opcode_name[553:559], 106: _Opcode_name[558:564],
107: _Opcode_name[559:565], 107: _Opcode_name[564:570],
108: _Opcode_name[565:571], 108: _Opcode_name[570:576],
109: _Opcode_name[571:577], 109: _Opcode_name[576:582],
110: _Opcode_name[577:583], 110: _Opcode_name[582:588],
111: _Opcode_name[583:588], 111: _Opcode_name[588:593],
112: _Opcode_name[588:594], 112: _Opcode_name[593:599],
113: _Opcode_name[594:600], 113: _Opcode_name[599:605],
114: _Opcode_name[600:606], 114: _Opcode_name[605:611],
115: _Opcode_name[606:612], 115: _Opcode_name[611:617],
116: _Opcode_name[612:618], 116: _Opcode_name[617:623],
117: _Opcode_name[618:624], 117: _Opcode_name[623:629],
118: _Opcode_name[624:630], 118: _Opcode_name[629:635],
119: _Opcode_name[630:635], 119: _Opcode_name[635:640],
120: _Opcode_name[635:641], 120: _Opcode_name[640:646],
121: _Opcode_name[641:647], 121: _Opcode_name[646:652],
122: _Opcode_name[647:653], 122: _Opcode_name[652:658],
123: _Opcode_name[653:659], 123: _Opcode_name[658:664],
124: _Opcode_name[659:665], 124: _Opcode_name[664:670],
125: _Opcode_name[665:671], 125: _Opcode_name[670:676],
126: _Opcode_name[671:677], 126: _Opcode_name[676:682],
127: _Opcode_name[677:682], 127: _Opcode_name[682:687],
128: _Opcode_name[682:688], 128: _Opcode_name[687:693],
129: _Opcode_name[688:694], 129: _Opcode_name[693:699],
130: _Opcode_name[694:700], 130: _Opcode_name[699:705],
131: _Opcode_name[700:706], 131: _Opcode_name[705:711],
132: _Opcode_name[706:712], 132: _Opcode_name[711:717],
133: _Opcode_name[712:718], 133: _Opcode_name[717:723],
134: _Opcode_name[718:724], 134: _Opcode_name[723:729],
135: _Opcode_name[724:729], 135: _Opcode_name[729:734],
136: _Opcode_name[729:738], 136: _Opcode_name[734:743],
137: _Opcode_name[738:744], 137: _Opcode_name[743:749],
139: _Opcode_name[744:747], 139: _Opcode_name[749:752],
140: _Opcode_name[747:753], 140: _Opcode_name[752:758],
141: _Opcode_name[753:757], 141: _Opcode_name[758:762],
142: _Opcode_name[757:762], 142: _Opcode_name[762:767],
144: _Opcode_name[762:768], 144: _Opcode_name[767:773],
145: _Opcode_name[768:771], 145: _Opcode_name[773:776],
146: _Opcode_name[771:773], 146: _Opcode_name[776:778],
147: _Opcode_name[773:776], 147: _Opcode_name[778:781],
151: _Opcode_name[776:781], 151: _Opcode_name[781:786],
152: _Opcode_name[781:789], 152: _Opcode_name[786:794],
153: _Opcode_name[789:793], 153: _Opcode_name[794:798],
154: _Opcode_name[793:796], 154: _Opcode_name[798:801],
155: _Opcode_name[796:802], 155: _Opcode_name[801:807],
156: _Opcode_name[802:805], 156: _Opcode_name[807:810],
157: _Opcode_name[805:808], 157: _Opcode_name[810:813],
158: _Opcode_name[808:811], 158: _Opcode_name[813:816],
159: _Opcode_name[811:814], 159: _Opcode_name[816:819],
160: _Opcode_name[814:817], 160: _Opcode_name[819:822],
161: _Opcode_name[817:820], 161: _Opcode_name[822:825],
162: _Opcode_name[820:823], 162: _Opcode_name[825:828],
168: _Opcode_name[823:826], 168: _Opcode_name[828:831],
169: _Opcode_name[826:829], 169: _Opcode_name[831:834],
170: _Opcode_name[829:832], 170: _Opcode_name[834:837],
171: _Opcode_name[832:839], 171: _Opcode_name[837:844],
172: _Opcode_name[839:845], 172: _Opcode_name[844:850],
177: _Opcode_name[845:847], 177: _Opcode_name[850:852],
179: _Opcode_name[847:855], 179: _Opcode_name[852:860],
180: _Opcode_name[855:866], 180: _Opcode_name[860:871],
181: _Opcode_name[866:868], 181: _Opcode_name[871:873],
182: _Opcode_name[868:871], 182: _Opcode_name[873:876],
183: _Opcode_name[871:873], 183: _Opcode_name[876:878],
184: _Opcode_name[873:876], 184: _Opcode_name[878:881],
185: _Opcode_name[876:879], 185: _Opcode_name[881:884],
186: _Opcode_name[879:882], 186: _Opcode_name[884:887],
187: _Opcode_name[882:888], 187: _Opcode_name[887:893],
192: _Opcode_name[888:892], 192: _Opcode_name[893:897],
193: _Opcode_name[892:898], 193: _Opcode_name[897:903],
194: _Opcode_name[898:907], 194: _Opcode_name[903:912],
195: _Opcode_name[907:915], 195: _Opcode_name[912:920],
196: _Opcode_name[915:924], 196: _Opcode_name[920:930],
197: _Opcode_name[924:934], 197: _Opcode_name[930:940],
198: _Opcode_name[934:943], 198: _Opcode_name[940:949],
200: _Opcode_name[943:949], 200: _Opcode_name[949:955],
202: _Opcode_name[949:953], 202: _Opcode_name[955:959],
203: _Opcode_name[953:959], 203: _Opcode_name[959:965],
204: _Opcode_name[959:963], 204: _Opcode_name[965:969],
205: _Opcode_name[963:969], 205: _Opcode_name[969:975],
206: _Opcode_name[969:977], 206: _Opcode_name[975:983],
207: _Opcode_name[977:983], 207: _Opcode_name[983:989],
208: _Opcode_name[983:990], 208: _Opcode_name[989:996],
209: _Opcode_name[990:1002], 209: _Opcode_name[996:1008],
210: _Opcode_name[1002:1008], 210: _Opcode_name[1008:1014],
211: _Opcode_name[1008:1018], 211: _Opcode_name[1014:1024],
216: _Opcode_name[1018:1024], 216: _Opcode_name[1024:1030],
217: _Opcode_name[1024:1030], 217: _Opcode_name[1030:1036],
219: _Opcode_name[1030:1037], 219: _Opcode_name[1036:1043],
} }
func (i Opcode) String() string { func (i Opcode) String() string {

View file

@ -4,6 +4,7 @@ import (
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
) )
// Nothing more to test here, really. // Nothing more to test here, really.
@ -18,3 +19,12 @@ func TestStringer(t *testing.T) {
assert.Equal(t, s, o.String()) assert.Equal(t, s, o.String())
} }
} }
func TestFromString(t *testing.T) {
_, err := FromString("abcdef")
require.Error(t, err)
op, err := FromString(MUL.String())
require.NoError(t, err)
require.Equal(t, MUL, op)
}

View file

@ -225,7 +225,9 @@ func (i *StructItem) Convert(typ StackItemType) (StackItem, error) {
case StructT: case StructT:
return i, nil return i, nil
case ArrayT: case ArrayT:
return NewArrayItem(i.value), nil arr := make([]StackItem, len(i.value))
copy(arr, i.value)
return NewArrayItem(arr), nil
case BooleanT: case BooleanT:
return NewBoolItem(i.Bool()), nil return NewBoolItem(i.Bool()), nil
default: default:
@ -641,7 +643,9 @@ func (i *ArrayItem) Convert(typ StackItemType) (StackItem, error) {
case ArrayT: case ArrayT:
return i, nil return i, nil
case StructT: case StructT:
return NewStructItem(i.value), nil arr := make([]StackItem, len(i.value))
copy(arr, i.value)
return NewStructItem(arr), nil
case BooleanT: case BooleanT:
return NewBoolItem(i.Bool()), nil return NewBoolItem(i.Bool()), nil
default: default:

View file

@ -338,7 +338,7 @@ func (v *VM) Run() error {
// check for breakpoint before executing the next instruction // check for breakpoint before executing the next instruction
ctx := v.Context() ctx := v.Context()
if ctx != nil && ctx.atBreakPoint() { if ctx != nil && ctx.atBreakPoint() {
v.state |= breakState v.state = breakState
} }
switch { switch {
case v.state.HasFlag(faultState): case v.state.HasFlag(faultState):
@ -376,7 +376,7 @@ func (v *VM) StepInto() error {
ctx := v.Context() ctx := v.Context()
if ctx == nil { if ctx == nil {
v.state |= haltState v.state = haltState
} }
if v.HasStopped() { if v.HasStopped() {
@ -518,17 +518,14 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
} }
switch op { switch op {
case opcode.PUSHM1, opcode.PUSH1, opcode.PUSH2, opcode.PUSH3, case opcode.PUSHM1, opcode.PUSH0, opcode.PUSH1, opcode.PUSH2, opcode.PUSH3,
opcode.PUSH4, opcode.PUSH5, opcode.PUSH6, opcode.PUSH7, opcode.PUSH4, opcode.PUSH5, opcode.PUSH6, opcode.PUSH7,
opcode.PUSH8, opcode.PUSH9, opcode.PUSH10, opcode.PUSH11, opcode.PUSH8, opcode.PUSH9, opcode.PUSH10, opcode.PUSH11,
opcode.PUSH12, opcode.PUSH13, opcode.PUSH14, opcode.PUSH15, opcode.PUSH12, opcode.PUSH13, opcode.PUSH14, opcode.PUSH15,
opcode.PUSH16: opcode.PUSH16:
val := int(op) - int(opcode.PUSH1) + 1 val := int(op) - int(opcode.PUSH0)
v.estack.PushVal(val) v.estack.PushVal(val)
case opcode.PUSH0:
v.estack.PushVal([]byte{})
case opcode.PUSHDATA1, opcode.PUSHDATA2, opcode.PUSHDATA4: case opcode.PUSHDATA1, opcode.PUSHDATA2, opcode.PUSHDATA4:
v.estack.PushVal(parameter) v.estack.PushVal(parameter)
@ -697,7 +694,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
} }
s := v.estack.Pop().Bytes() s := v.estack.Pop().Bytes()
if t := len(s); l > t { if t := len(s); l > t {
l = t panic("size is too big")
} }
v.estack.PushVal(NewBufferItem(s[:l])) v.estack.PushVal(NewBufferItem(s[:l]))
@ -984,46 +981,28 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
case opcode.NEWARRAY, opcode.NEWARRAYT: case opcode.NEWARRAY, opcode.NEWARRAYT:
item := v.estack.Pop() item := v.estack.Pop()
switch t := item.value.(type) { n := item.BigInt().Int64()
case *StructItem: if n > MaxArraySize {
arr := make([]StackItem, len(t.value)) panic("too long array")
copy(arr, t.value)
v.estack.PushVal(&ArrayItem{arr})
case *ArrayItem:
v.estack.PushVal(t)
default:
n := item.BigInt().Int64()
if n > MaxArraySize {
panic("too long array")
}
typ := BooleanT
if op == opcode.NEWARRAYT {
typ = StackItemType(parameter[0])
}
items := makeArrayOfType(int(n), typ)
v.estack.PushVal(&ArrayItem{items})
} }
typ := AnyT
if op == opcode.NEWARRAYT {
typ = StackItemType(parameter[0])
}
items := makeArrayOfType(int(n), typ)
v.estack.PushVal(&ArrayItem{items})
case opcode.NEWSTRUCT0: case opcode.NEWSTRUCT0:
v.estack.PushVal(&StructItem{[]StackItem{}}) v.estack.PushVal(&StructItem{[]StackItem{}})
case opcode.NEWSTRUCT: case opcode.NEWSTRUCT:
item := v.estack.Pop() item := v.estack.Pop()
switch t := item.value.(type) { n := item.BigInt().Int64()
case *ArrayItem: if n > MaxArraySize {
arr := make([]StackItem, len(t.value)) panic("too long struct")
copy(arr, t.value)
v.estack.PushVal(&StructItem{arr})
case *StructItem:
v.estack.PushVal(t)
default:
n := item.BigInt().Int64()
if n > MaxArraySize {
panic("too long struct")
}
items := makeArrayOfType(int(n), BooleanT)
v.estack.PushVal(&StructItem{items})
} }
items := makeArrayOfType(int(n), AnyT)
v.estack.PushVal(&StructItem{items})
case opcode.APPEND: case opcode.APPEND:
itemElem := v.estack.Pop() itemElem := v.estack.Pop()

View file

@ -372,7 +372,7 @@ func appendBigStruct(size uint16) []opcode.Opcode {
return append(prog, return append(prog,
opcode.INITSSLOT, 1, opcode.INITSSLOT, 1,
opcode.PUSHINT16, opcode.Opcode(size), opcode.Opcode(size>>8), // LE opcode.PUSHINT16, opcode.Opcode(size), opcode.Opcode(size>>8), // LE
opcode.PACK, opcode.NEWSTRUCT, opcode.PACK, opcode.CONVERT, opcode.Opcode(StructT),
opcode.STSFLD0, opcode.LDSFLD0, opcode.STSFLD0, opcode.LDSFLD0,
opcode.DUP, opcode.DUP,
opcode.PUSH0, opcode.NEWARRAY, opcode.PUSH0, opcode.NEWARRAY,
@ -401,20 +401,19 @@ func TestStackLimit(t *testing.T) {
{opcode.NEWARRAY, 3}, // array + 2 items {opcode.NEWARRAY, 3}, // array + 2 items
{opcode.STSFLD0, 3}, {opcode.STSFLD0, 3},
{opcode.LDSFLD0, 4}, {opcode.LDSFLD0, 4},
{opcode.NEWSTRUCT, 6}, // all items are copied {opcode.NEWMAP, 5},
{opcode.NEWMAP, 7}, {opcode.DUP, 6},
{opcode.DUP, 8}, {opcode.PUSH2, 7},
{opcode.PUSH2, 9}, {opcode.LDSFLD0, 8},
{opcode.LDSFLD0, 10}, {opcode.SETITEM, 6}, // -3 items and 1 new element in map
{opcode.SETITEM, 8}, // -3 items and 1 new element in map {opcode.DUP, 7},
{opcode.DUP, 9}, {opcode.PUSH2, 8},
{opcode.PUSH2, 10}, {opcode.LDSFLD0, 9},
{opcode.LDSFLD0, 11}, {opcode.SETITEM, 6}, // -3 items and no new elements in map
{opcode.SETITEM, 8}, // -3 items and no new elements in map {opcode.DUP, 7},
{opcode.DUP, 9}, {opcode.PUSH2, 8},
{opcode.PUSH2, 10}, {opcode.REMOVE, 5}, // as we have right after NEWMAP
{opcode.REMOVE, 7}, // as we have right after NEWMAP {opcode.DROP, 4}, // DROP map with no elements
{opcode.DROP, 6}, // DROP map with no elements
} }
prog := make([]opcode.Opcode, len(expected)+2) prog := make([]opcode.Opcode, len(expected)+2)
@ -1188,45 +1187,37 @@ func TestNEWARRAYArray(t *testing.T) {
prog := makeProgram(opcode.NEWARRAY) prog := makeProgram(opcode.NEWARRAY)
t.Run("ByteArray", getTestFuncForVM(prog, NewArrayItem([]StackItem{}), []byte{})) t.Run("ByteArray", getTestFuncForVM(prog, NewArrayItem([]StackItem{}), []byte{}))
t.Run("BadSize", getTestFuncForVM(prog, nil, MaxArraySize+1)) t.Run("BadSize", getTestFuncForVM(prog, nil, MaxArraySize+1))
t.Run("Integer", getTestFuncForVM(prog, []StackItem{NewBoolItem(false)}, 1)) t.Run("Integer", getTestFuncForVM(prog, []StackItem{NullItem{}}, 1))
arr := []StackItem{makeStackItem(42)}
t.Run("Array", getTestFuncForVM(prog, arr, arr))
t.Run("Struct", getTestFuncForVM(prog, arr, NewStructItem(arr)))
} }
func testNEWARRAYIssue437(t *testing.T, i1, i2 opcode.Opcode, appended bool) { func testNEWARRAYIssue437(t *testing.T, i1 opcode.Opcode, t2 StackItemType, appended bool) {
prog := makeProgram( prog := makeProgram(
opcode.PUSH2, i1, opcode.PUSH2, i1,
opcode.DUP, opcode.PUSH3, opcode.APPEND, opcode.DUP, opcode.PUSH3, opcode.APPEND,
opcode.INITSSLOT, 1, opcode.INITSSLOT, 1,
opcode.STSFLD0, opcode.LDSFLD0, i2, opcode.STSFLD0, opcode.LDSFLD0, opcode.CONVERT, opcode.Opcode(t2),
opcode.DUP, opcode.PUSH4, opcode.APPEND, opcode.DUP, opcode.PUSH4, opcode.APPEND,
opcode.LDSFLD0, opcode.PUSH5, opcode.APPEND) opcode.LDSFLD0, opcode.PUSH5, opcode.APPEND)
vm := load(prog)
vm.Run()
arr := makeArrayOfType(4, BooleanT) arr := makeArrayOfType(4, AnyT)
arr[2] = makeStackItem(3) arr[2] = makeStackItem(3)
arr[3] = makeStackItem(4) arr[3] = makeStackItem(4)
if appended { if appended {
arr = append(arr, makeStackItem(5)) arr = append(arr, makeStackItem(5))
} }
assert.Equal(t, false, vm.HasFailed()) if t2 == ArrayT {
assert.Equal(t, 1, vm.estack.Len()) runWithArgs(t, prog, &ArrayItem{arr})
if i2 == opcode.NEWARRAY {
assert.Equal(t, &ArrayItem{arr}, vm.estack.Pop().value)
} else { } else {
assert.Equal(t, &StructItem{arr}, vm.estack.Pop().value) runWithArgs(t, prog, &StructItem{arr})
} }
} }
func TestNEWARRAYIssue437(t *testing.T) { func TestNEWARRAYIssue437(t *testing.T) {
t.Run("Array+Array", func(t *testing.T) { testNEWARRAYIssue437(t, opcode.NEWARRAY, opcode.NEWARRAY, true) }) t.Run("Array+Array", func(t *testing.T) { testNEWARRAYIssue437(t, opcode.NEWARRAY, ArrayT, true) })
t.Run("Struct+Struct", func(t *testing.T) { testNEWARRAYIssue437(t, opcode.NEWSTRUCT, opcode.NEWSTRUCT, true) }) t.Run("Struct+Struct", func(t *testing.T) { testNEWARRAYIssue437(t, opcode.NEWSTRUCT, StructT, true) })
t.Run("Array+Struct", func(t *testing.T) { testNEWARRAYIssue437(t, opcode.NEWARRAY, opcode.NEWSTRUCT, false) }) t.Run("Array+Struct", func(t *testing.T) { testNEWARRAYIssue437(t, opcode.NEWARRAY, StructT, false) })
t.Run("Struct+Array", func(t *testing.T) { testNEWARRAYIssue437(t, opcode.NEWSTRUCT, opcode.NEWARRAY, false) }) t.Run("Struct+Array", func(t *testing.T) { testNEWARRAYIssue437(t, opcode.NEWSTRUCT, ArrayT, false) })
} }
func TestNEWARRAYT(t *testing.T) { func TestNEWARRAYT(t *testing.T) {
@ -1247,11 +1238,7 @@ func TestNEWSTRUCT(t *testing.T) {
prog := makeProgram(opcode.NEWSTRUCT) prog := makeProgram(opcode.NEWSTRUCT)
t.Run("ByteArray", getTestFuncForVM(prog, NewStructItem([]StackItem{}), []byte{})) t.Run("ByteArray", getTestFuncForVM(prog, NewStructItem([]StackItem{}), []byte{}))
t.Run("BadSize", getTestFuncForVM(prog, nil, MaxArraySize+1)) t.Run("BadSize", getTestFuncForVM(prog, nil, MaxArraySize+1))
t.Run("Integer", getTestFuncForVM(prog, NewStructItem([]StackItem{NewBoolItem(false)}), 1)) t.Run("Integer", getTestFuncForVM(prog, NewStructItem([]StackItem{NullItem{}}), 1))
arr := []StackItem{makeStackItem(42)}
t.Run("Array", getTestFuncForVM(prog, NewStructItem(arr), NewArrayItem(arr)))
t.Run("Struct", getTestFuncForVM(prog, NewStructItem(arr), NewStructItem(arr)))
} }
func TestAPPEND(t *testing.T) { func TestAPPEND(t *testing.T) {
@ -1827,7 +1814,7 @@ func TestLEFT(t *testing.T) {
t.Run("NoString", getTestFuncForVM(prog, nil, 2)) t.Run("NoString", getTestFuncForVM(prog, nil, 2))
t.Run("NegativeLen", getTestFuncForVM(prog, nil, "abcdef", -1)) t.Run("NegativeLen", getTestFuncForVM(prog, nil, "abcdef", -1))
t.Run("Good", getTestFuncForVM(prog, NewBufferItem([]byte("ab")), "abcdef", 2)) t.Run("Good", getTestFuncForVM(prog, NewBufferItem([]byte("ab")), "abcdef", 2))
t.Run("GoodBigLen", getTestFuncForVM(prog, NewBufferItem([]byte("abcdef")), "abcdef", 8)) t.Run("BadBigLen", getTestFuncForVM(prog, nil, "abcdef", 8))
} }
func TestRIGHT(t *testing.T) { func TestRIGHT(t *testing.T) {
@ -1903,14 +1890,12 @@ func TestREVERSEITEMS(t *testing.T) {
t.Run("Buffer", getTestFuncForVM(prog, NewBufferItem([]byte{3, 2, 1}), NewBufferItem([]byte{1, 2, 3}))) t.Run("Buffer", getTestFuncForVM(prog, NewBufferItem([]byte{3, 2, 1}), NewBufferItem([]byte{1, 2, 3})))
} }
func testREVERSEITEMSIssue437(t *testing.T, i1, i2 opcode.Opcode, reversed bool) { func testREVERSEITEMSIssue437(t *testing.T, i1 opcode.Opcode, t2 StackItemType, reversed bool) {
prog := makeProgram( prog := makeProgram(
opcode.PUSH0, i1, opcode.PUSH0, i1,
opcode.DUP, opcode.PUSH1, opcode.APPEND, opcode.DUP, opcode.PUSH1, opcode.APPEND,
opcode.DUP, opcode.PUSH2, opcode.APPEND, opcode.DUP, opcode.PUSH2, opcode.APPEND,
opcode.DUP, i2, opcode.REVERSEITEMS) opcode.DUP, opcode.CONVERT, opcode.Opcode(t2), opcode.REVERSEITEMS)
vm := load(prog)
vm.Run()
arr := make([]StackItem, 2) arr := make([]StackItem, 2)
if reversed { if reversed {
@ -1920,20 +1905,19 @@ func testREVERSEITEMSIssue437(t *testing.T, i1, i2 opcode.Opcode, reversed bool)
arr[0] = makeStackItem(1) arr[0] = makeStackItem(1)
arr[1] = makeStackItem(2) arr[1] = makeStackItem(2)
} }
assert.Equal(t, false, vm.HasFailed())
assert.Equal(t, 1, vm.estack.Len())
if i1 == opcode.NEWARRAY { if i1 == opcode.NEWARRAY {
assert.Equal(t, &ArrayItem{arr}, vm.estack.Pop().value) runWithArgs(t, prog, &ArrayItem{arr})
} else { } else {
assert.Equal(t, &StructItem{arr}, vm.estack.Pop().value) runWithArgs(t, prog, &StructItem{arr})
} }
} }
func TestREVERSEITEMSIssue437(t *testing.T) { func TestREVERSEITEMSIssue437(t *testing.T) {
t.Run("Array+Array", func(t *testing.T) { testREVERSEITEMSIssue437(t, opcode.NEWARRAY, opcode.NEWARRAY, true) }) t.Run("Array+Array", func(t *testing.T) { testREVERSEITEMSIssue437(t, opcode.NEWARRAY, ArrayT, true) })
t.Run("Struct+Struct", func(t *testing.T) { testREVERSEITEMSIssue437(t, opcode.NEWSTRUCT, opcode.NEWSTRUCT, true) }) t.Run("Struct+Struct", func(t *testing.T) { testREVERSEITEMSIssue437(t, opcode.NEWSTRUCT, StructT, true) })
t.Run("Array+Struct", func(t *testing.T) { testREVERSEITEMSIssue437(t, opcode.NEWARRAY, opcode.NEWSTRUCT, false) }) t.Run("Array+Struct", func(t *testing.T) { testREVERSEITEMSIssue437(t, opcode.NEWARRAY, StructT, false) })
t.Run("Struct+Array", func(t *testing.T) { testREVERSEITEMSIssue437(t, opcode.NEWSTRUCT, opcode.NEWARRAY, false) }) t.Run("Struct+Array", func(t *testing.T) { testREVERSEITEMSIssue437(t, opcode.NEWSTRUCT, ArrayT, false) })
} }
func TestREVERSEITEMSGoodOneElem(t *testing.T) { func TestREVERSEITEMSGoodOneElem(t *testing.T) {
@ -2471,7 +2455,9 @@ func makeProgram(opcodes ...opcode.Opcode) []byte {
func load(prog []byte) *VM { func load(prog []byte) *VM {
vm := New() vm := New()
vm.LoadScript(prog) if len(prog) != 0 {
vm.LoadScript(prog)
}
return vm return vm
} }