vm: duplicate an item in Dup

TestDupByteArray and TestDupInt were failing before this patch.
This commit is contained in:
Roman Khimov 2019-12-17 16:38:42 +03:00
parent c596a6b273
commit 60dfa05b19
4 changed files with 86 additions and 1 deletions

View file

@ -159,6 +159,11 @@ func (c *Context) Value() interface{} {
return c return c
} }
// Dup implements StackItem interface.
func (c *Context) Dup() StackItem {
return c
}
func (c *Context) atBreakPoint() bool { func (c *Context) atBreakPoint() bool {
for _, n := range c.breakPoints { for _, n := range c.breakPoints {
if n == c.ip { if n == c.ip {

View file

@ -346,7 +346,7 @@ func (s *Stack) Dup(n int) *Element {
} }
return &Element{ return &Element{
value: e.value, value: e.value.Dup(),
} }
} }

View file

@ -15,6 +15,8 @@ import (
type StackItem interface { type StackItem interface {
fmt.Stringer fmt.Stringer
Value() interface{} Value() interface{}
// Dup duplicates current StackItem.
Dup() StackItem
} }
func makeStackItem(v interface{}) StackItem { func makeStackItem(v interface{}) StackItem {
@ -107,6 +109,12 @@ func (i *StructItem) String() string {
return "Struct" return "Struct"
} }
// Dup implements StackItem interface.
func (i *StructItem) Dup() StackItem {
// it's a reference type, so no copying here.
return i
}
// Clone returns a Struct with all Struct fields copied by value. // Clone returns a Struct with all Struct fields copied by value.
// Array fields are still copied by reference. // Array fields are still copied by reference.
func (i *StructItem) Clone() *StructItem { func (i *StructItem) Clone() *StructItem {
@ -148,6 +156,12 @@ func (i *BigIntegerItem) String() string {
return "BigInteger" return "BigInteger"
} }
// Dup implements StackItem interface.
func (i *BigIntegerItem) Dup() StackItem {
n := new(big.Int)
return &BigIntegerItem{n.Set(i.value)}
}
// MarshalJSON implements the json.Marshaler interface. // MarshalJSON implements the json.Marshaler interface.
func (i *BigIntegerItem) MarshalJSON() ([]byte, error) { func (i *BigIntegerItem) MarshalJSON() ([]byte, error) {
return json.Marshal(i.value) return json.Marshal(i.value)
@ -179,6 +193,11 @@ func (i *BoolItem) String() string {
return "Bool" return "Bool"
} }
// Dup implements StackItem interface.
func (i *BoolItem) Dup() StackItem {
return &BoolItem{i.value}
}
// ByteArrayItem represents a byte array on the stack. // ByteArrayItem represents a byte array on the stack.
type ByteArrayItem struct { type ByteArrayItem struct {
value []byte value []byte
@ -205,6 +224,13 @@ func (i *ByteArrayItem) String() string {
return "ByteArray" return "ByteArray"
} }
// Dup implements StackItem interface.
func (i *ByteArrayItem) Dup() StackItem {
a := make([]byte, len(i.value))
copy(a, i.value)
return &ByteArrayItem{a}
}
// ArrayItem represents a new ArrayItem object. // ArrayItem represents a new ArrayItem object.
type ArrayItem struct { type ArrayItem struct {
value []StackItem value []StackItem
@ -231,6 +257,12 @@ func (i *ArrayItem) String() string {
return "Array" return "Array"
} }
// Dup implements StackItem interface.
func (i *ArrayItem) Dup() StackItem {
// reference type
return i
}
// MapItem represents Map object. // MapItem represents Map object.
type MapItem struct { type MapItem struct {
value map[interface{}]StackItem value map[interface{}]StackItem
@ -259,6 +291,12 @@ func (i *MapItem) Has(key StackItem) (ok bool) {
return return
} }
// Dup implements StackItem interface.
func (i *MapItem) Dup() StackItem {
// reference type
return i
}
// Add adds key-value pair to the map. // Add adds key-value pair to the map.
func (i *MapItem) Add(key, value StackItem) { func (i *MapItem) Add(key, value StackItem) {
i.value[toMapKey(key)] = value i.value[toMapKey(key)] = value
@ -300,6 +338,12 @@ func (i *InteropItem) String() string {
return "InteropItem" return "InteropItem"
} }
// Dup implements StackItem interface.
func (i *InteropItem) Dup() StackItem {
// reference type
return i
}
// MarshalJSON implements the json.Marshaler interface. // MarshalJSON implements the json.Marshaler interface.
func (i *InteropItem) MarshalJSON() ([]byte, error) { func (i *InteropItem) MarshalJSON() ([]byte, error) {
return json.Marshal(i.value) return json.Marshal(i.value)

View file

@ -2519,6 +2519,42 @@ func TestXSWAPBad2(t *testing.T) {
checkVMFailed(t, vm) checkVMFailed(t, vm)
} }
func TestDupInt(t *testing.T) {
prog := makeProgram(opcode.DUP, opcode.ABS)
vm := load(prog)
vm.estack.PushVal(-1)
runVM(t, vm)
assert.Equal(t, 2, vm.estack.Len())
assert.Equal(t, int64(1), vm.estack.Pop().BigInt().Int64())
assert.Equal(t, int64(-1), vm.estack.Pop().BigInt().Int64())
}
func TestDupByteArray(t *testing.T) {
prog := makeProgram(opcode.PUSHBYTES2, 1, 0,
opcode.DUP,
opcode.PUSH1,
opcode.LEFT,
opcode.PUSHBYTES1, 2,
opcode.CAT)
vm := load(prog)
runVM(t, vm)
assert.Equal(t, 2, vm.estack.Len())
assert.Equal(t, []byte{0x01, 0x02}, vm.estack.Pop().Bytes())
assert.Equal(t, []byte{0x01, 0x00}, vm.estack.Pop().Bytes())
}
func TestDupBool(t *testing.T) {
prog := makeProgram(opcode.PUSH0, opcode.NOT,
opcode.DUP,
opcode.PUSH1, opcode.NOT,
opcode.BOOLAND)
vm := load(prog)
runVM(t, vm)
assert.Equal(t, 2, vm.estack.Len())
assert.Equal(t, false, vm.estack.Pop().Bool())
assert.Equal(t, true, vm.estack.Pop().Bool())
}
func makeProgram(opcodes ...opcode.Opcode) []byte { func makeProgram(opcodes ...opcode.Opcode) []byte {
prog := make([]byte, len(opcodes)+1) // RET prog := make([]byte, len(opcodes)+1) // RET
for i := 0; i < len(opcodes); i++ { for i := 0; i < len(opcodes); i++ {