Merge pull request #405 from nspcc-dev/fix/array-reference-type

VM: compare Array by reference and clone structs on append
This commit is contained in:
Roman Khimov 2019-09-23 14:54:32 +03:00 committed by GitHub
commit 2f9e7ca037
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 61 additions and 2 deletions

View file

@ -75,6 +75,21 @@ func (i *StructItem) String() string {
return "Struct" 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. // BigIntegerItem represents a big integer on the stack.
type BigIntegerItem struct { type BigIntegerItem struct {
value *big.Int value *big.Int

View file

@ -454,6 +454,12 @@ func (v *VM) execute(ctx *Context, op Instruction) {
if a == nil { if a == nil {
panic("no second-to-the-top element found") 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)) v.estack.PushVal(reflect.DeepEqual(a, b))
// Bit operations. // Bit operations.
@ -642,14 +648,19 @@ func (v *VM) execute(ctx *Context, op Instruction) {
itemElem := v.estack.Pop() itemElem := v.estack.Pop()
arrElem := 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) { switch t := arrElem.value.(type) {
case *ArrayItem: case *ArrayItem:
arr := t.Value().([]StackItem) arr := t.Value().([]StackItem)
arr = append(arr, itemElem.value) arr = append(arr, val)
t.value = arr t.value = arr
case *StructItem: case *StructItem:
arr := t.Value().([]StackItem) arr := t.Value().([]StackItem)
arr = append(arr, itemElem.value) arr = append(arr, val)
t.value = arr t.value = arr
default: default:
panic("APPEND: not of underlying type Array") panic("APPEND: not of underlying type Array")

View file

@ -405,6 +405,27 @@ func TestEQUALGoodInteger(t *testing.T) {
assert.Equal(t, &BoolItem{true}, vm.estack.Pop().value) 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) { func TestNumEqual(t *testing.T) {
prog := makeProgram(NUMEQUAL) prog := makeProgram(NUMEQUAL)
vm := load(prog) vm := load(prog)
@ -538,6 +559,18 @@ func TestAPPENDStruct(t *testing.T) {
assert.Equal(t, &StructItem{[]StackItem{makeStackItem(5)}}, vm.estack.Pop().value) 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) { func TestAPPENDBadNoArguments(t *testing.T) {
prog := makeProgram(APPEND) prog := makeProgram(APPEND)
vm := load(prog) vm := load(prog)