From 99f1d761cac011630ca7d6a85827f43e49bb28c4 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Wed, 18 Sep 2019 14:03:15 +0300 Subject: [PATCH 1/2] vm: clone Struct on APPEND --- pkg/vm/stack_item.go | 15 +++++++++++++++ pkg/vm/vm.go | 9 +++++++-- pkg/vm/vm_test.go | 12 ++++++++++++ 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/pkg/vm/stack_item.go b/pkg/vm/stack_item.go index 18ed92f19..6dff26040 100644 --- a/pkg/vm/stack_item.go +++ b/pkg/vm/stack_item.go @@ -75,6 +75,21 @@ func (i *StructItem) String() string { return "Struct" } +// Clone returns a Struct with all Struct fields copied by value. +// Array fields are still copied by reference. +func (i *StructItem) Clone() *StructItem { + ret := &StructItem{make([]StackItem, len(i.value))} + for j := range i.value { + switch t := i.value[j].(type) { + case *StructItem: + ret.value[j] = t.Clone() + default: + ret.value[j] = t + } + } + return ret +} + // BigIntegerItem represents a big integer on the stack. type BigIntegerItem struct { value *big.Int diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go index f0647d8d6..b533b1347 100644 --- a/pkg/vm/vm.go +++ b/pkg/vm/vm.go @@ -642,14 +642,19 @@ func (v *VM) execute(ctx *Context, op Instruction) { itemElem := v.estack.Pop() arrElem := v.estack.Pop() + val := itemElem.value + if t, ok := itemElem.value.(*StructItem); ok { + val = t.Clone() + } + switch t := arrElem.value.(type) { case *ArrayItem: arr := t.Value().([]StackItem) - arr = append(arr, itemElem.value) + arr = append(arr, val) t.value = arr case *StructItem: arr := t.Value().([]StackItem) - arr = append(arr, itemElem.value) + arr = append(arr, val) t.value = arr default: panic("APPEND: not of underlying type Array") diff --git a/pkg/vm/vm_test.go b/pkg/vm/vm_test.go index 2193afcb9..fd806b162 100644 --- a/pkg/vm/vm_test.go +++ b/pkg/vm/vm_test.go @@ -538,6 +538,18 @@ func TestAPPENDStruct(t *testing.T) { assert.Equal(t, &StructItem{[]StackItem{makeStackItem(5)}}, vm.estack.Pop().value) } +func TestAPPENDCloneStruct(t *testing.T) { + prog := makeProgram(DUP, PUSH0, NEWSTRUCT, TOALTSTACK, DUPFROMALTSTACK, APPEND, FROMALTSTACK, PUSH1, APPEND) + vm := load(prog) + vm.estack.Push(&Element{value: &ArrayItem{}}) + vm.Run() + assert.Equal(t, false, vm.state.HasFlag(faultState)) + assert.Equal(t, 1, vm.estack.Len()) + assert.Equal(t, &ArrayItem{[]StackItem{ + &StructItem{[]StackItem{}}, + }}, vm.estack.Pop().value) +} + func TestAPPENDBadNoArguments(t *testing.T) { prog := makeProgram(APPEND) vm := load(prog) From a88a8e13fcda131a0bd8fc6391f06091ca2a4d17 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Wed, 18 Sep 2019 14:10:10 +0300 Subject: [PATCH 2/2] vm: compare Array by reference in EQUAL --- pkg/vm/vm.go | 6 ++++++ pkg/vm/vm_test.go | 21 +++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go index b533b1347..3592291a9 100644 --- a/pkg/vm/vm.go +++ b/pkg/vm/vm.go @@ -454,6 +454,12 @@ func (v *VM) execute(ctx *Context, op Instruction) { if a == nil { panic("no second-to-the-top element found") } + if ta, ok := a.value.(*ArrayItem); ok { + if tb, ok := b.value.(*ArrayItem); ok { + v.estack.PushVal(ta == tb) + break + } + } v.estack.PushVal(reflect.DeepEqual(a, b)) // Bit operations. diff --git a/pkg/vm/vm_test.go b/pkg/vm/vm_test.go index fd806b162..9d8e1ca0f 100644 --- a/pkg/vm/vm_test.go +++ b/pkg/vm/vm_test.go @@ -405,6 +405,27 @@ func TestEQUALGoodInteger(t *testing.T) { assert.Equal(t, &BoolItem{true}, vm.estack.Pop().value) } +func TestEQUALArrayTrue(t *testing.T) { + prog := makeProgram(DUP, EQUAL) + vm := load(prog) + vm.estack.PushVal([]StackItem{}) + vm.Run() + assert.Equal(t, false, vm.state.HasFlag(faultState)) + assert.Equal(t, 1, vm.estack.Len()) + assert.Equal(t, &BoolItem{true}, vm.estack.Pop().value) +} + +func TestEQUALArrayFalse(t *testing.T) { + prog := makeProgram(EQUAL) + vm := load(prog) + vm.estack.PushVal([]StackItem{}) + vm.estack.PushVal([]StackItem{}) + vm.Run() + assert.Equal(t, false, vm.state.HasFlag(faultState)) + assert.Equal(t, 1, vm.estack.Len()) + assert.Equal(t, &BoolItem{false}, vm.estack.Pop().value) +} + func TestNumEqual(t *testing.T) { prog := makeProgram(NUMEQUAL) vm := load(prog)