Merge pull request #382 from nspcc-dev/feature/implicit-stackitem-conversion

VM: implement stack item conversion

Fixes #372.

Also added tests for NOT and SIGN to check new behavior.
Also fixed a bug with Bool casting: it should be != 0 instead of == 1.
This commit is contained in:
Roman Khimov 2019-09-10 17:42:40 +03:00 committed by GitHub
commit c6d3313f82
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 182 additions and 4 deletions

View file

@ -63,6 +63,11 @@ func (e *Element) BigInt() *big.Int {
switch t := e.value.(type) { switch t := e.value.(type) {
case *BigIntegerItem: case *BigIntegerItem:
return t.value return t.value
case *BoolItem:
if t.value {
return big.NewInt(1)
}
return big.NewInt(0)
default: default:
b := t.Value().([]uint8) b := t.Value().([]uint8)
return new(big.Int).SetBytes(util.ArrayReverse(b)) return new(big.Int).SetBytes(util.ArrayReverse(b))
@ -72,16 +77,41 @@ func (e *Element) BigInt() *big.Int {
// Bool attempts to get the underlying value of the element as a boolean. // Bool attempts to get the underlying value of the element as a boolean.
// Will panic if the assertion failed which will be caught by the VM. // Will panic if the assertion failed which will be caught by the VM.
func (e *Element) Bool() bool { func (e *Element) Bool() bool {
if v, ok := e.value.Value().(*big.Int); ok { switch t := e.value.(type) {
return v.Int64() == 1 case *BigIntegerItem:
return t.value.Int64() != 0
case *BoolItem:
return t.value
case *ArrayItem, *StructItem:
return true
case *ByteArrayItem:
for _, b := range t.value {
if b != 0 {
return true
}
}
return false
default:
panic("can't convert to bool: " + t.String())
} }
return e.value.Value().(bool)
} }
// Bytes attempts to get the underlying value of the element as a byte array. // Bytes attempts to get the underlying value of the element as a byte array.
// Will panic if the assertion failed which will be caught by the VM. // Will panic if the assertion failed which will be caught by the VM.
func (e *Element) Bytes() []byte { func (e *Element) Bytes() []byte {
return e.value.Value().([]byte) switch t := e.value.(type) {
case *ByteArrayItem:
return t.value
case *BigIntegerItem:
return util.ArrayReverse(t.value.Bytes()) // neoVM returns in LE
case *BoolItem:
if t.value {
return []byte{1}
}
return []byte{0}
default:
panic("can't convert to []byte: " + t.String())
}
} }
// Array attempts to get the underlying value of the element as an array of // Array attempts to get the underlying value of the element as an array of

View file

@ -111,6 +111,67 @@ func TestPushData4(t *testing.T) {
} }
func TestNOTNoArgument(t *testing.T) {
prog := makeProgram(NOT)
vm := load(prog)
vm.Run()
assert.Equal(t, true, vm.state.HasFlag(faultState))
}
func TestNOTBool(t *testing.T) {
prog := makeProgram(NOT)
vm := load(prog)
vm.estack.PushVal(false)
vm.Run()
assert.Equal(t, false, vm.state.HasFlag(faultState))
assert.Equal(t, &BoolItem{true}, vm.estack.Pop().value)
}
func TestNOTNonZeroInt(t *testing.T) {
prog := makeProgram(NOT)
vm := load(prog)
vm.estack.PushVal(3)
vm.Run()
assert.Equal(t, false, vm.state.HasFlag(faultState))
assert.Equal(t, &BoolItem{false}, vm.estack.Pop().value)
}
func TestNOTArray(t *testing.T) {
prog := makeProgram(NOT)
vm := load(prog)
vm.estack.PushVal([]StackItem{})
vm.Run()
assert.Equal(t, false, vm.state.HasFlag(faultState))
assert.Equal(t, &BoolItem{false}, vm.estack.Pop().value)
}
func TestNOTStruct(t *testing.T) {
prog := makeProgram(NOT)
vm := load(prog)
vm.estack.Push(NewElement(&StructItem{[]StackItem{}}))
vm.Run()
assert.Equal(t, false, vm.state.HasFlag(faultState))
assert.Equal(t, &BoolItem{false}, vm.estack.Pop().value)
}
func TestNOTByteArray0(t *testing.T) {
prog := makeProgram(NOT)
vm := load(prog)
vm.estack.PushVal([]byte{0, 0})
vm.Run()
assert.Equal(t, false, vm.state.HasFlag(faultState))
assert.Equal(t, &BoolItem{true}, vm.estack.Pop().value)
}
func TestNOTByteArray1(t *testing.T) {
prog := makeProgram(NOT)
vm := load(prog)
vm.estack.PushVal([]byte{0, 1})
vm.Run()
assert.Equal(t, false, vm.state.HasFlag(faultState))
assert.Equal(t, &BoolItem{false}, vm.estack.Pop().value)
}
func TestAdd(t *testing.T) { func TestAdd(t *testing.T) {
prog := makeProgram(ADD) prog := makeProgram(ADD)
vm := load(prog) vm := load(prog)
@ -312,6 +373,71 @@ func TestNEWSTRUCTWrongType(t *testing.T) {
assert.Equal(t, true, vm.state.HasFlag(faultState)) assert.Equal(t, true, vm.state.HasFlag(faultState))
} }
func TestSIGNNoArgument(t *testing.T) {
prog := makeProgram(SIGN)
vm := load(prog)
vm.Run()
assert.Equal(t, true, vm.state.HasFlag(faultState))
}
func TestSIGNWrongType(t *testing.T) {
prog := makeProgram(SIGN)
vm := load(prog)
vm.estack.PushVal([]StackItem{})
vm.Run()
assert.Equal(t, true, vm.state.HasFlag(faultState))
}
func TestSIGNBool(t *testing.T) {
prog := makeProgram(SIGN)
vm := load(prog)
vm.estack.PushVal(false)
vm.Run()
assert.Equal(t, false, vm.state.HasFlag(faultState))
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, &BigIntegerItem{big.NewInt(0)}, vm.estack.Pop().value)
}
func TestSIGNPositiveInt(t *testing.T) {
prog := makeProgram(SIGN)
vm := load(prog)
vm.estack.PushVal(1)
vm.Run()
assert.Equal(t, false, vm.state.HasFlag(faultState))
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, &BigIntegerItem{big.NewInt(1)}, vm.estack.Pop().value)
}
func TestSIGNNegativeInt(t *testing.T) {
prog := makeProgram(SIGN)
vm := load(prog)
vm.estack.PushVal(-1)
vm.Run()
assert.Equal(t, false, vm.state.HasFlag(faultState))
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, &BigIntegerItem{big.NewInt(-1)}, vm.estack.Pop().value)
}
func TestSIGNZero(t *testing.T) {
prog := makeProgram(SIGN)
vm := load(prog)
vm.estack.PushVal(0)
vm.Run()
assert.Equal(t, false, vm.state.HasFlag(faultState))
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, &BigIntegerItem{big.NewInt(0)}, vm.estack.Pop().value)
}
func TestSIGNByteArray(t *testing.T) {
prog := makeProgram(SIGN)
vm := load(prog)
vm.estack.PushVal([]byte{0, 1})
vm.Run()
assert.Equal(t, false, vm.state.HasFlag(faultState))
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, &BigIntegerItem{big.NewInt(1)}, vm.estack.Pop().value)
}
func TestAppCall(t *testing.T) { func TestAppCall(t *testing.T) {
prog := []byte{byte(APPCALL)} prog := []byte{byte(APPCALL)}
hash := util.Uint160{} hash := util.Uint160{}
@ -603,6 +729,28 @@ func TestCATGood(t *testing.T) {
assert.Equal(t, []byte("abcdef"), vm.estack.Peek(0).Bytes()) assert.Equal(t, []byte("abcdef"), vm.estack.Peek(0).Bytes())
} }
func TestCATInt0ByteArray(t *testing.T) {
prog := makeProgram(CAT)
vm := load(prog)
vm.estack.PushVal(0)
vm.estack.PushVal([]byte{})
vm.Run()
assert.Equal(t, false, vm.state.HasFlag(faultState))
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, &ByteArrayItem{[]byte{}}, vm.estack.Pop().value)
}
func TestCATByteArrayInt1(t *testing.T) {
prog := makeProgram(CAT)
vm := load(prog)
vm.estack.PushVal([]byte{})
vm.estack.PushVal(1)
vm.Run()
assert.Equal(t, false, vm.state.HasFlag(faultState))
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, &ByteArrayItem{[]byte{1}}, vm.estack.Pop().value)
}
func TestSUBSTRBadNoArgs(t *testing.T) { func TestSUBSTRBadNoArgs(t *testing.T) {
prog := makeProgram(SUBSTR) prog := makeProgram(SUBSTR)
vm := load(prog) vm := load(prog)