Merge branch 'vm' into dauTT/vm-implement-MIN-MAX-WITHIN-opcodes-230

This commit is contained in:
dauTT 2019-04-03 20:36:54 +02:00 committed by GitHub
commit 6d2fb5d89f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 420 additions and 3 deletions

View file

@ -112,9 +112,37 @@ func (i *Int) Abs() (*Int, error) {
return b, nil return b, nil
} }
// Lte returns a bool value from the comparison of two integers, a and b.
// value is true if a <= b.
// value is false if a > b.
func (i *Int) Lte(s *Int) bool {
return i.Value().Cmp(s.Value()) != 1
}
// Gte returns a bool value from the comparison of two integers, a and b.
// value is true if a >= b.
// value is false if a < b.
func (i *Int) Gte(s *Int) bool {
return i.Value().Cmp(s.Value()) != -1
}
// Lt returns a bool value from the comparison of two integers, a and b.
// value is true if a < b.
// value is false if a >= b.
func (i *Int) Lt(s *Int) bool {
return i.Value().Cmp(s.Value()) == -1
}
// Gt returns a bool value from the comparison of two integers, a and b.
// value is true if a > b.
// value is false if a <= b.
func (i *Int) Gt(s *Int) bool {
return i.Value().Cmp(s.Value()) == 1
}
// Min returns the mininum between two integers. // Min returns the mininum between two integers.
func Min(a *Int, b *Int) *Int { func Min(a *Int, b *Int) *Int {
if a.Value().Cmp(b.Value()) == -1 { if a.Lte(b) {
return a return a
} }
return b return b
@ -123,7 +151,7 @@ func Min(a *Int, b *Int) *Int {
// Max returns the maximun between two integers. // Max returns the maximun between two integers.
func Max(a *Int, b *Int) *Int { func Max(a *Int, b *Int) *Int {
if a.Value().Cmp(b.Value()) == 1 { if a.Gte(b) {
return a return a
} }
return b return b
@ -134,6 +162,6 @@ func Max(a *Int, b *Int) *Int {
// range [a,b) (left-inclusive). // range [a,b) (left-inclusive).
func (i *Int) Within(a *Int, b *Int) bool { func (i *Int) Within(a *Int, b *Int) bool {
// i >= a && i < b // i >= a && i < b
return !(i.Value().Cmp(a.Value()) == -1) && i.Value().Cmp(b.Value()) == -1 return i.Gte(a) && i.Lt(b)
} }

View file

@ -30,3 +30,17 @@ func (b *Boolean) Value() bool {
func (b *Boolean) Not() *Boolean { func (b *Boolean) Not() *Boolean {
return NewBoolean(!b.Value()) return NewBoolean(!b.Value())
} }
// And returns a Boolean whose underlying value is obtained
// by applying the && operator to two Booleans' values.
func (b *Boolean) And(a *Boolean) *Boolean {
c := b.Value() && a.Value()
return NewBoolean(c)
}
// Or returns a Boolean whose underlying value is obtained
// by applying the || operator to two Booleans' values.
func (b *Boolean) Or(a *Boolean) *Boolean {
c := b.Value() || a.Value()
return NewBoolean(c)
}

View file

@ -8,6 +8,14 @@ var opFunc = map[stack.Instruction]stackInfo{
stack.MIN: Min, stack.MIN: Min,
stack.MAX: Max, stack.MAX: Max,
stack.WITHIN: Within, stack.WITHIN: Within,
stack.NUMEQUAL: NumEqual,
stack.NUMNOTEQUAL: NumNotEqual,
stack.BOOLAND: BoolAnd,
stack.BOOLOR: BoolOr,
stack.LT: Lt,
stack.LTE: Lte,
stack.GT: Gt,
stack.GTE: Gte,
stack.SHR: Shr, stack.SHR: Shr,
stack.SHL: Shl, stack.SHL: Shl,
stack.INC: Inc, stack.INC: Inc,

View file

@ -173,6 +173,38 @@ func Mul(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rst
return NONE, nil return NONE, nil
} }
// NumEqual pops two Items off of the stack and pushes a boolean to the stack
// whose value is true iff the the two Items are equal.
// Returns an error if either items cannot be casted to an integer.
func NumEqual(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) {
operandA, operandB, err := popTwoIntegers(ctx)
if err != nil {
return FAULT, err
}
res := operandA.Equal(operandB)
ctx.Estack.Push(stack.NewBoolean(res))
return NONE, nil
}
// NumNotEqual pops two Items off of the stack and pushes a boolean to the stack
// whose value is true iff the two Items are not equal.
// Returns an error if either items cannot be casted to an integer.
func NumNotEqual(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) {
operandA, operandB, err := popTwoIntegers(ctx)
if err != nil {
return FAULT, err
}
res := operandA.Equal(operandB)
ctx.Estack.Push(stack.NewBoolean(!res))
return NONE, nil
}
// Min pops two integers, a and b, off of the stack and pushes an integer to the stack // Min pops two integers, a and b, off of the stack and pushes an integer to the stack
// whose value is is the minum between a and b's value. // whose value is is the minum between a and b's value.
// Returns an error if either items cannot be casted to an integer // Returns an error if either items cannot be casted to an integer
@ -254,6 +286,44 @@ func Not(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rst
return NONE, nil return NONE, nil
} }
// BoolAnd pops two booleans off of the stack and pushes a boolean to the stack
// whose value is true iff both booleans' values are true.
// Returns an error if either items cannot be casted to an boolean
func BoolAnd(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) {
bool1, bool2, err := popTwoBooleans(ctx)
if err != nil {
return FAULT, err
}
res := bool1.And(bool2)
if err != nil {
return FAULT, err
}
ctx.Estack.Push(res)
return NONE, nil
}
// BoolOr pops two booleans off of the stack and pushes a boolean to the stack
// whose value is true iff at least one of the two booleans' value is true.
// Returns an error if either items cannot be casted to an boolean
func BoolOr(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) {
bool1, bool2, err := popTwoBooleans(ctx)
if err != nil {
return FAULT, err
}
res := bool1.Or(bool2)
if err != nil {
return FAULT, err
}
ctx.Estack.Push(res)
return NONE, nil
}
// Sign puts the sign of the top stack Item on top of the stack. // Sign puts the sign of the top stack Item on top of the stack.
// If value is negative, put -1; // If value is negative, put -1;
// If positive, put 1; // If positive, put 1;
@ -295,6 +365,38 @@ func Negate(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation,
return NONE, nil return NONE, nil
} }
// Lte pops two integers, a and b, off of the stack and pushes a boolean the stack
// whose value is true if a's value is less than or equal to b's value.
// Returns an error if either items cannot be casted to an integer
func Lte(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) {
operandA, operandB, err := popTwoIntegers(ctx)
if err != nil {
return FAULT, err
}
res := operandB.Lte(operandA)
ctx.Estack.Push(stack.NewBoolean(res))
return NONE, nil
}
// Gte pops two integers, a and b, off of the stack and pushes a boolean the stack
// whose value is true if a's value is greated than or equal to b's value.
// Returns an error if either items cannot be casted to an integer
func Gte(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) {
operandA, operandB, err := popTwoIntegers(ctx)
if err != nil {
return FAULT, err
}
res := operandB.Gte(operandA)
ctx.Estack.Push(stack.NewBoolean(res))
return NONE, nil
}
// Shl pops two integers, a and b, off of the stack and pushes an integer to the stack // Shl pops two integers, a and b, off of the stack and pushes an integer to the stack
// whose value is the b's value shift to the left by a's value bits. // whose value is the b's value shift to the left by a's value bits.
// Returns an error if either items cannot be casted to an integer // Returns an error if either items cannot be casted to an integer
@ -335,6 +437,38 @@ func Shr(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rst
return NONE, nil return NONE, nil
} }
// Lt pops two integers, a and b, off of the stack and pushes a boolean the stack
// whose value is true if a's value is less than b's value.
// Returns an error if either items cannot be casted to an integer
func Lt(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) {
operandA, operandB, err := popTwoIntegers(ctx)
if err != nil {
return FAULT, err
}
res := operandB.Lt(operandA)
ctx.Estack.Push(stack.NewBoolean(res))
return NONE, nil
}
// Gt pops two integers, a and b, off of the stack and pushes a boolean the stack
// whose value is true if a's value is greated than b's value.
// Returns an error if either items cannot be casted to an integer
func Gt(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) {
operandA, operandB, err := popTwoIntegers(ctx)
if err != nil {
return FAULT, err
}
res := operandB.Gt(operandA)
ctx.Estack.Push(stack.NewBoolean(res))
return NONE, nil
}
func popTwoIntegers(ctx *stack.Context) (*stack.Int, *stack.Int, error) { func popTwoIntegers(ctx *stack.Context) (*stack.Int, *stack.Int, error) {
operandA, err := ctx.Estack.PopInt() operandA, err := ctx.Estack.PopInt()
if err != nil { if err != nil {
@ -378,3 +512,16 @@ func popTwoByteArrays(ctx *stack.Context) (*stack.ByteArray, *stack.ByteArray, e
} }
return ba1, ba2, nil return ba1, ba2, nil
} }
func popTwoBooleans(ctx *stack.Context) (*stack.Boolean, *stack.Boolean, error) {
bool1, err := ctx.Estack.PopBoolean()
if err != nil {
return nil, nil, err
}
bool2, err := ctx.Estack.PopBoolean()
if err != nil {
return nil, nil, err
}
return bool1, bool2, nil
}

View file

@ -277,6 +277,64 @@ func TestNotOp(t *testing.T) {
assert.Equal(t, true, item.Value()) assert.Equal(t, true, item.Value())
} }
func TestNumEqual(t *testing.T) {
v := VM{}
a, err := stack.NewInt(big.NewInt(6))
if err != nil {
t.Fail()
}
b, err := stack.NewInt(big.NewInt(6))
if err != nil {
t.Fail()
}
ctx := stack.NewContext([]byte{})
ctx.Estack.Push(a).Push(b)
v.executeOp(stack.NUMEQUAL, ctx)
// Stack should have one item
assert.Equal(t, 1, ctx.Estack.Len())
item, err := ctx.Estack.PopBoolean()
if err != nil {
t.Fail()
}
assert.Equal(t, true, item.Value())
}
func TestNumNotEqual(t *testing.T) {
v := VM{}
a, err := stack.NewInt(big.NewInt(5))
if err != nil {
t.Fail()
}
b, err := stack.NewInt(big.NewInt(6))
if err != nil {
t.Fail()
}
ctx := stack.NewContext([]byte{})
ctx.Estack.Push(a).Push(b)
v.executeOp(stack.NUMNOTEQUAL, ctx)
// Stack should have one item
assert.Equal(t, 1, ctx.Estack.Len())
item, err := ctx.Estack.PopBoolean()
if err != nil {
t.Fail()
}
assert.Equal(t, true, item.Value())
}
func TestSignOp(t *testing.T) { func TestSignOp(t *testing.T) {
v := VM{} v := VM{}
@ -327,6 +385,64 @@ func TestNegateOp(t *testing.T) {
assert.Equal(t, int64(20), item.Value().Int64()) assert.Equal(t, int64(20), item.Value().Int64())
} }
func TestLteOp(t *testing.T) {
v := VM{}
a, err := stack.NewInt(big.NewInt(10))
assert.Nil(t, err)
b, err := stack.NewInt(big.NewInt(10))
assert.Nil(t, err)
ctx := stack.NewContext([]byte{})
ctx.Estack.Push(a).Push(b)
// b is the first item pop.
// a is the second item pop.
// we perform a <= b and place
// the result on top of the evaluation
// stack
v.executeOp(stack.LTE, ctx)
// Stack should have one item
assert.Equal(t, 1, ctx.Estack.Len())
item, err := ctx.Estack.PopBoolean()
assert.Nil(t, err)
assert.Equal(t, true, item.Value())
}
func TestGteOp(t *testing.T) {
v := VM{}
a, err := stack.NewInt(big.NewInt(10))
assert.Nil(t, err)
b, err := stack.NewInt(big.NewInt(2))
assert.Nil(t, err)
ctx := stack.NewContext([]byte{})
ctx.Estack.Push(a).Push(b)
// b is the first item pop.
// a is the second item pop.
// we perform a >= b and place
// the result on top of the evaluation
// stack
v.executeOp(stack.GTE, ctx)
// Stack should have one item
assert.Equal(t, 1, ctx.Estack.Len())
item, err := ctx.Estack.PopBoolean()
assert.Nil(t, err)
assert.Equal(t, true, item.Value())
}
func TestShlOp(t *testing.T) { func TestShlOp(t *testing.T) {
v := VM{} v := VM{}
@ -395,6 +511,110 @@ func TestShrOp(t *testing.T) {
assert.Equal(t, int64(2), item.Value().Int64()) assert.Equal(t, int64(2), item.Value().Int64())
} }
func TestBoolAndOp(t *testing.T) {
v := VM{}
a := stack.NewBoolean(true)
b := stack.NewBoolean(true)
ctx := stack.NewContext([]byte{})
ctx.Estack.Push(a).Push(b)
v.executeOp(stack.BOOLAND, ctx)
// Stack should have one item
assert.Equal(t, 1, ctx.Estack.Len())
item, err := ctx.Estack.PopBoolean()
if err != nil {
t.Fail()
}
assert.Equal(t, true, item.Value())
}
func TestBoolOrOp(t *testing.T) {
v := VM{}
a := stack.NewBoolean(false)
b := stack.NewBoolean(true)
ctx := stack.NewContext([]byte{})
ctx.Estack.Push(a).Push(b)
v.executeOp(stack.BOOLOR, ctx)
// Stack should have one item
assert.Equal(t, 1, ctx.Estack.Len())
item, err := ctx.Estack.PopBoolean()
if err != nil {
t.Fail()
}
assert.Equal(t, true, item.Value())
}
func TestLtOp(t *testing.T) {
v := VM{}
a, err := stack.NewInt(big.NewInt(10))
assert.NoError(t, err)
b, err := stack.NewInt(big.NewInt(2))
assert.NoError(t, err)
ctx := stack.NewContext([]byte{})
ctx.Estack.Push(a).Push(b)
// b is the first item pop.
// a is the second item pop.
// we perform a < b and place
// the result on top of the evaluation
// stack
v.executeOp(stack.LT, ctx)
// Stack should have one item
assert.Equal(t, 1, ctx.Estack.Len())
item, err := ctx.Estack.PopBoolean()
assert.NoError(t, err)
assert.Equal(t, false, item.Value())
}
func TestGtOp(t *testing.T) {
v := VM{}
a, err := stack.NewInt(big.NewInt(10))
assert.NoError(t, err)
b, err := stack.NewInt(big.NewInt(2))
assert.NoError(t, err)
ctx := stack.NewContext([]byte{})
ctx.Estack.Push(a).Push(b)
// b is the first item pop.
// a is the second item pop.
// we perform a > b and place
// the result on top of the evaluation
// stack
v.executeOp(stack.GT, ctx)
// Stack should have one item
assert.Equal(t, 1, ctx.Estack.Len())
item, err := ctx.Estack.PopBoolean()
assert.NoError(t, err)
assert.Equal(t, true, item.Value())
}
func TestMinOp(t *testing.T) { func TestMinOp(t *testing.T) {
v := VM{} v := VM{}