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:
commit
c6d3313f82
2 changed files with 182 additions and 4 deletions
|
@ -63,6 +63,11 @@ func (e *Element) BigInt() *big.Int {
|
|||
switch t := e.value.(type) {
|
||||
case *BigIntegerItem:
|
||||
return t.value
|
||||
case *BoolItem:
|
||||
if t.value {
|
||||
return big.NewInt(1)
|
||||
}
|
||||
return big.NewInt(0)
|
||||
default:
|
||||
b := t.Value().([]uint8)
|
||||
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.
|
||||
// Will panic if the assertion failed which will be caught by the VM.
|
||||
func (e *Element) Bool() bool {
|
||||
if v, ok := e.value.Value().(*big.Int); ok {
|
||||
return v.Int64() == 1
|
||||
switch t := e.value.(type) {
|
||||
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.
|
||||
// Will panic if the assertion failed which will be caught by the VM.
|
||||
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
|
||||
|
|
|
@ -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) {
|
||||
prog := makeProgram(ADD)
|
||||
vm := load(prog)
|
||||
|
@ -312,6 +373,71 @@ func TestNEWSTRUCTWrongType(t *testing.T) {
|
|||
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) {
|
||||
prog := []byte{byte(APPCALL)}
|
||||
hash := util.Uint160{}
|
||||
|
@ -603,6 +729,28 @@ func TestCATGood(t *testing.T) {
|
|||
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) {
|
||||
prog := makeProgram(SUBSTR)
|
||||
vm := load(prog)
|
||||
|
|
Loading…
Reference in a new issue