From e79a17442b199cfd30e0b34c3746d59ec067fd03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Wed, 14 Apr 2021 17:07:04 -0700 Subject: [PATCH 01/11] fix multiplication and division: properly return underfow errors --- runtime/interpreter/value.go | 40 +++++++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/runtime/interpreter/value.go b/runtime/interpreter/value.go index 5b6104a81c..d0e6e2f0c1 100644 --- a/runtime/interpreter/value.go +++ b/runtime/interpreter/value.go @@ -1222,20 +1222,24 @@ func (v Int8Value) Mul(other NumberValue) NumberValue { // INT32-C if v > 0 { if o > 0 { + // positive * positive = positive. overflow? if v > (math.MaxInt8 / o) { panic(OverflowError{}) } } else { + // positive * negative = negative. underflow? if o < (math.MinInt8 / v) { - panic(OverflowError{}) + panic(UnderflowError{}) } } } else { if o > 0 { + // negative * positive = negative. underflow? if v < (math.MinInt8 / o) { - panic(OverflowError{}) + panic(UnderflowError{}) } } else { + // negative * negative = positive. overflow? if (v != 0) && (o < (math.MaxInt8 / v)) { panic(OverflowError{}) } @@ -1463,20 +1467,24 @@ func (v Int16Value) Mul(other NumberValue) NumberValue { // INT32-C if v > 0 { if o > 0 { + // positive * positive = positive. overflow? if v > (math.MaxInt16 / o) { panic(OverflowError{}) } } else { + // positive * negative = negative. underflow? if o < (math.MinInt16 / v) { - panic(OverflowError{}) + panic(UnderflowError{}) } } } else { if o > 0 { + // negative * positive = negative. underflow? if v < (math.MinInt16 / o) { - panic(OverflowError{}) + panic(UnderflowError{}) } } else { + // negative * negative = positive. overflow? if (v != 0) && (o < (math.MaxInt16 / v)) { panic(OverflowError{}) } @@ -1706,20 +1714,24 @@ func (v Int32Value) Mul(other NumberValue) NumberValue { // INT32-C if v > 0 { if o > 0 { + // positive * positive = positive. overflow? if v > (math.MaxInt32 / o) { panic(OverflowError{}) } } else { + // positive * negative = negative. underflow? if o < (math.MinInt32 / v) { - panic(OverflowError{}) + panic(UnderflowError{}) } } } else { if o > 0 { + // negative * positive = negative. underflow? if v < (math.MinInt32 / o) { - panic(OverflowError{}) + panic(UnderflowError{}) } } else { + // negative * negative = positive. overflow? if (v != 0) && (o < (math.MaxInt32 / v)) { panic(OverflowError{}) } @@ -1953,20 +1965,24 @@ func (v Int64Value) Mul(other NumberValue) NumberValue { // INT32-C if v > 0 { if o > 0 { + // positive * positive = positive. overflow? if v > (math.MaxInt64 / o) { panic(OverflowError{}) } } else { + // positive * negative = negative. underflow? if o < (math.MinInt64 / v) { - panic(OverflowError{}) + panic(UnderflowError{}) } } } else { if o > 0 { + // negative * positive = negative. underflow? if v < (math.MinInt64 / o) { - panic(OverflowError{}) + panic(UnderflowError{}) } } else { + // negative * negative = positive. overflow? if (v != 0) && (o < (math.MaxInt64 / v)) { panic(OverflowError{}) } @@ -5111,7 +5127,9 @@ func (v Fix64Value) Mul(other NumberValue) NumberValue { result := new(big.Int).Mul(a, b) result.Div(result, sema.Fix64FactorBig) - if !result.IsInt64() { + if result.Cmp(minInt64Big) < 0 { + panic(UnderflowError{}) + } else if result.Cmp(maxInt64Big) > 0 { panic(OverflowError{}) } @@ -5127,7 +5145,9 @@ func (v Fix64Value) Div(other NumberValue) NumberValue { result := new(big.Int).Mul(a, sema.Fix64FactorBig) result.Div(result, b) - if !result.IsInt64() { + if result.Cmp(minInt64Big) < 0 { + panic(UnderflowError{}) + } else if result.Cmp(maxInt64Big) > 0 { panic(OverflowError{}) } From fab7a1a59cb0f4d35e96e0cbc61ef4903064169c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Wed, 14 Apr 2021 17:08:14 -0700 Subject: [PATCH 02/11] implement saturating arithmetic for integer and fixed-point values --- runtime/interpreter/value.go | 738 ++++++++++++++++++++++++++++++++++- 1 file changed, 734 insertions(+), 4 deletions(-) diff --git a/runtime/interpreter/value.go b/runtime/interpreter/value.go index d0e6e2f0c1..31e463897f 100644 --- a/runtime/interpreter/value.go +++ b/runtime/interpreter/value.go @@ -1197,6 +1197,17 @@ func (v Int8Value) Plus(other NumberValue) NumberValue { return v + o } +func (v Int8Value) SaturatingPlus(other NumberValue) NumberValue { + o := other.(Int8Value) + // INT32-C + if (o > 0) && (v > (math.MaxInt8 - o)) { + return Int8Value(math.MaxInt8) + } else if (o < 0) && (v < (math.MinInt8 - o)) { + return Int8Value(math.MinInt8) + } + return v + o +} + func (v Int8Value) Minus(other NumberValue) NumberValue { o := other.(Int8Value) // INT32-C @@ -1208,6 +1219,17 @@ func (v Int8Value) Minus(other NumberValue) NumberValue { return v - o } +func (v Int8Value) SaturatingMinus(other NumberValue) NumberValue { + o := other.(Int8Value) + // INT32-C + if (o > 0) && (v < (math.MinInt8 + o)) { + return Int8Value(math.MaxInt8) + } else if (o < 0) && (v > (math.MaxInt8 + o)) { + return Int8Value(math.MinInt8) + } + return v - o +} + func (v Int8Value) Mod(other NumberValue) NumberValue { o := other.(Int8Value) // INT33-C @@ -1248,6 +1270,37 @@ func (v Int8Value) Mul(other NumberValue) NumberValue { return v * o } +func (v Int8Value) SaturatingMul(other NumberValue) NumberValue { + o := other.(Int8Value) + // INT32-C + if v > 0 { + if o > 0 { + // positive * positive = positive. overflow? + if v > (math.MaxInt8 / o) { + return Int8Value(math.MaxInt8) + } + } else { + // positive * negative = negative. underflow? + if o < (math.MinInt8 / v) { + return Int8Value(math.MinInt8) + } + } + } else { + if o > 0 { + // negative * positive = negative. underflow? + if v < (math.MinInt8 / o) { + return Int8Value(math.MinInt8) + } + } else { + // negative * negative = positive. overflow? + if (v != 0) && (o < (math.MaxInt8 / v)) { + return Int8Value(math.MaxInt8) + } + } + } + return v * o +} + func (v Int8Value) Div(other NumberValue) NumberValue { o := other.(Int8Value) // INT33-C @@ -1260,6 +1313,18 @@ func (v Int8Value) Div(other NumberValue) NumberValue { return v / o } +func (v Int8Value) SaturatingDiv(other NumberValue) NumberValue { + o := other.(Int8Value) + // INT33-C + // https://golang.org/ref/spec#Integer_operators + if o == 0 { + panic(DivisionByZeroError{}) + } else if (v == math.MinInt8) && (o == -1) { + return Int8Value(math.MaxInt8) + } + return v / o +} + func (v Int8Value) Less(other NumberValue) BoolValue { return v < other.(Int8Value) } @@ -1442,6 +1507,17 @@ func (v Int16Value) Plus(other NumberValue) NumberValue { return v + o } +func (v Int16Value) SaturatingPlus(other NumberValue) NumberValue { + o := other.(Int16Value) + // INT32-C + if (o > 0) && (v > (math.MaxInt16 - o)) { + return Int16Value(math.MaxInt16) + } else if (o < 0) && (v < (math.MinInt16 - o)) { + return Int16Value(math.MinInt16) + } + return v + o +} + func (v Int16Value) Minus(other NumberValue) NumberValue { o := other.(Int16Value) // INT32-C @@ -1453,6 +1529,17 @@ func (v Int16Value) Minus(other NumberValue) NumberValue { return v - o } +func (v Int16Value) SaturatingMinus(other NumberValue) NumberValue { + o := other.(Int16Value) + // INT32-C + if (o > 0) && (v < (math.MinInt16 + o)) { + return Int16Value(math.MaxInt16) + } else if (o < 0) && (v > (math.MaxInt16 + o)) { + return Int16Value(math.MinInt16) + } + return v - o +} + func (v Int16Value) Mod(other NumberValue) NumberValue { o := other.(Int16Value) // INT33-C @@ -1493,6 +1580,37 @@ func (v Int16Value) Mul(other NumberValue) NumberValue { return v * o } +func (v Int16Value) SaturatingMul(other NumberValue) NumberValue { + o := other.(Int16Value) + // INT32-C + if v > 0 { + if o > 0 { + // positive * positive = positive. overflow? + if v > (math.MaxInt16 / o) { + return Int16Value(math.MaxInt16) + } + } else { + // positive * negative = negative. underflow? + if o < (math.MinInt16 / v) { + return Int16Value(math.MinInt16) + } + } + } else { + if o > 0 { + // negative * positive = negative. underflow? + if v < (math.MinInt16 / o) { + return Int16Value(math.MinInt16) + } + } else { + // negative * negative = positive. overflow? + if (v != 0) && (o < (math.MaxInt16 / v)) { + return Int16Value(math.MaxInt16) + } + } + } + return v * o +} + func (v Int16Value) Div(other NumberValue) NumberValue { o := other.(Int16Value) // INT33-C @@ -1505,6 +1623,18 @@ func (v Int16Value) Div(other NumberValue) NumberValue { return v / o } +func (v Int16Value) SaturatingDiv(other NumberValue) NumberValue { + o := other.(Int16Value) + // INT33-C + // https://golang.org/ref/spec#Integer_operators + if o == 0 { + panic(DivisionByZeroError{}) + } else if (v == math.MinInt16) && (o == -1) { + return Int16Value(math.MaxInt16) + } + return v / o +} + func (v Int16Value) Less(other NumberValue) BoolValue { return v < other.(Int16Value) } @@ -1689,6 +1819,17 @@ func (v Int32Value) Plus(other NumberValue) NumberValue { return v + o } +func (v Int32Value) SaturatingPlus(other NumberValue) NumberValue { + o := other.(Int32Value) + // INT32-C + if (o > 0) && (v > (math.MaxInt32 - o)) { + return Int32Value(math.MaxInt32) + } else if (o < 0) && (v < (math.MinInt32 - o)) { + return Int32Value(math.MinInt32) + } + return v + o +} + func (v Int32Value) Minus(other NumberValue) NumberValue { o := other.(Int32Value) // INT32-C @@ -1700,6 +1841,17 @@ func (v Int32Value) Minus(other NumberValue) NumberValue { return v - o } +func (v Int32Value) SaturatingMinus(other NumberValue) NumberValue { + o := other.(Int32Value) + // INT32-C + if (o > 0) && (v < (math.MinInt32 + o)) { + return Int32Value(math.MaxInt32) + } else if (o < 0) && (v > (math.MaxInt32 + o)) { + return Int32Value(math.MinInt32) + } + return v - o +} + func (v Int32Value) Mod(other NumberValue) NumberValue { o := other.(Int32Value) // INT33-C @@ -1740,6 +1892,37 @@ func (v Int32Value) Mul(other NumberValue) NumberValue { return v * o } +func (v Int32Value) SaturatingMul(other NumberValue) NumberValue { + o := other.(Int32Value) + // INT32-C + if v > 0 { + if o > 0 { + // positive * positive = positive. overflow? + if v > (math.MaxInt32 / o) { + return Int32Value(math.MaxInt32) + } + } else { + // positive * negative = negative. underflow? + if o < (math.MinInt32 / v) { + return Int32Value(math.MinInt32) + } + } + } else { + if o > 0 { + // negative * positive = negative. underflow? + if v < (math.MinInt32 / o) { + return Int32Value(math.MinInt32) + } + } else { + // negative * negative = positive. overflow? + if (v != 0) && (o < (math.MaxInt32 / v)) { + return Int32Value(math.MaxInt32) + } + } + } + return v * o +} + func (v Int32Value) Div(other NumberValue) NumberValue { o := other.(Int32Value) // INT33-C @@ -1752,6 +1935,18 @@ func (v Int32Value) Div(other NumberValue) NumberValue { return v / o } +func (v Int32Value) SaturatingDiv(other NumberValue) NumberValue { + o := other.(Int32Value) + // INT33-C + // https://golang.org/ref/spec#Integer_operators + if o == 0 { + panic(DivisionByZeroError{}) + } else if (v == math.MinInt32) && (o == -1) { + return Int32Value(math.MaxInt32) + } + return v / o +} + func (v Int32Value) Less(other NumberValue) BoolValue { return v < other.(Int32Value) } @@ -1940,6 +2135,17 @@ func (v Int64Value) Plus(other NumberValue) NumberValue { return Int64Value(safeAddInt64(int64(v), int64(o))) } +func (v Int64Value) SaturatingPlus(other NumberValue) NumberValue { + o := other.(Int64Value) + // INT32-C + if (o > 0) && (v > (math.MaxInt64 - o)) { + return Int64Value(math.MaxInt64) + } else if (o < 0) && (v < (math.MinInt64 - o)) { + return Int64Value(math.MinInt64) + } + return v + o +} + func (v Int64Value) Minus(other NumberValue) NumberValue { o := other.(Int64Value) // INT32-C @@ -1951,6 +2157,17 @@ func (v Int64Value) Minus(other NumberValue) NumberValue { return v - o } +func (v Int64Value) SaturatingMinus(other NumberValue) NumberValue { + o := other.(Int64Value) + // INT32-C + if (o > 0) && (v < (math.MinInt64 + o)) { + return Int64Value(math.MaxInt64) + } else if (o < 0) && (v > (math.MaxInt64 + o)) { + return Int64Value(math.MinInt64) + } + return v - o +} + func (v Int64Value) Mod(other NumberValue) NumberValue { o := other.(Int64Value) // INT33-C @@ -1991,6 +2208,37 @@ func (v Int64Value) Mul(other NumberValue) NumberValue { return v * o } +func (v Int64Value) SaturatingMul(other NumberValue) NumberValue { + o := other.(Int64Value) + // INT32-C + if v > 0 { + if o > 0 { + // positive * positive = positive. overflow? + if v > (math.MaxInt64 / o) { + return Int64Value(math.MaxInt64) + } + } else { + // positive * negative = negative. underflow? + if o < (math.MinInt64 / v) { + return Int64Value(math.MinInt64) + } + } + } else { + if o > 0 { + // negative * positive = negative. underflow? + if v < (math.MinInt64 / o) { + return Int64Value(math.MinInt64) + } + } else { + // negative * negative = positive. overflow? + if (v != 0) && (o < (math.MaxInt64 / v)) { + return Int64Value(math.MaxInt64) + } + } + } + return v * o +} + func (v Int64Value) Div(other NumberValue) NumberValue { o := other.(Int64Value) // INT33-C @@ -2003,6 +2251,18 @@ func (v Int64Value) Div(other NumberValue) NumberValue { return v / o } +func (v Int64Value) SaturatingDiv(other NumberValue) NumberValue { + o := other.(Int64Value) + // INT33-C + // https://golang.org/ref/spec#Integer_operators + if o == 0 { + panic(DivisionByZeroError{}) + } else if (v == math.MinInt64) && (o == -1) { + return Int64Value(math.MaxInt64) + } + return v / o +} + func (v Int64Value) Less(other NumberValue) BoolValue { return v < other.(Int64Value) } @@ -2212,6 +2472,30 @@ func (v Int128Value) Plus(other NumberValue) NumberValue { return Int128Value{res} } +func (v Int128Value) SaturatingPlus(other NumberValue) NumberValue { + o := other.(Int128Value) + // Given that this value is backed by an arbitrary size integer, + // we can just add and check the range of the result. + // + // If Go gains a native int128 type and we switch this value + // to be based on it, then we need to follow INT32-C: + // + // if (o > 0) && (v > (Int128TypeMaxIntBig - o)) { + // ... + // } else if (o < 0) && (v < (Int128TypeMinIntBig - o)) { + // ... + // } + // + res := new(big.Int) + res.Add(v.BigInt, o.BigInt) + if res.Cmp(sema.Int128TypeMinIntBig) < 0 { + return Int128Value{sema.Int128TypeMinIntBig} + } else if res.Cmp(sema.Int128TypeMaxIntBig) > 0 { + return Int128Value{sema.Int128TypeMaxIntBig} + } + return Int128Value{res} +} + func (v Int128Value) Minus(other NumberValue) NumberValue { o := other.(Int128Value) // Given that this value is backed by an arbitrary size integer, @@ -2236,6 +2520,30 @@ func (v Int128Value) Minus(other NumberValue) NumberValue { return Int128Value{res} } +func (v Int128Value) SaturatingMinus(other NumberValue) NumberValue { + o := other.(Int128Value) + // Given that this value is backed by an arbitrary size integer, + // we can just subtract and check the range of the result. + // + // If Go gains a native int128 type and we switch this value + // to be based on it, then we need to follow INT32-C: + // + // if (o > 0) && (v < (Int128TypeMinIntBig + o)) { + // ... + // } else if (o < 0) && (v > (Int128TypeMaxIntBig + o)) { + // ... + // } + // + res := new(big.Int) + res.Sub(v.BigInt, o.BigInt) + if res.Cmp(sema.Int128TypeMinIntBig) < 0 { + return Int128Value{sema.Int128TypeMinIntBig} + } else if res.Cmp(sema.Int128TypeMaxIntBig) > 0 { + return Int128Value{sema.Int128TypeMaxIntBig} + } + return Int128Value{res} +} + func (v Int128Value) Mod(other NumberValue) NumberValue { o := other.(Int128Value) res := new(big.Int) @@ -2259,6 +2567,18 @@ func (v Int128Value) Mul(other NumberValue) NumberValue { return Int128Value{res} } +func (v Int128Value) SaturatingMul(other NumberValue) NumberValue { + o := other.(Int128Value) + res := new(big.Int) + res.Mul(v.BigInt, o.BigInt) + if res.Cmp(sema.Int128TypeMinIntBig) < 0 { + return Int128Value{sema.Int128TypeMinIntBig} + } else if res.Cmp(sema.Int128TypeMaxIntBig) > 0 { + return Int128Value{sema.Int128TypeMaxIntBig} + } + return Int128Value{res} +} + func (v Int128Value) Div(other NumberValue) NumberValue { o := other.(Int128Value) res := new(big.Int) @@ -2279,6 +2599,26 @@ func (v Int128Value) Div(other NumberValue) NumberValue { return Int128Value{res} } +func (v Int128Value) SaturatingDiv(other NumberValue) NumberValue { + o := other.(Int128Value) + res := new(big.Int) + // INT33-C: + // if o == 0 { + // ... + // } else if (v == Int128TypeMinIntBig) && (o == -1) { + // ... + // } + if o.BigInt.Cmp(res) == 0 { + panic(DivisionByZeroError{}) + } + res.SetInt64(-1) + if (v.BigInt.Cmp(sema.Int128TypeMinIntBig) == 0) && (o.BigInt.Cmp(res) == 0) { + return Int128Value{sema.Int128TypeMaxIntBig} + } + res.Div(v.BigInt, o.BigInt) + return Int128Value{res} +} + func (v Int128Value) Less(other NumberValue) BoolValue { cmp := v.BigInt.Cmp(other.(Int128Value).BigInt) return cmp == -1 @@ -2513,6 +2853,30 @@ func (v Int256Value) Plus(other NumberValue) NumberValue { return Int256Value{res} } +func (v Int256Value) SaturatingPlus(other NumberValue) NumberValue { + o := other.(Int256Value) + // Given that this value is backed by an arbitrary size integer, + // we can just add and check the range of the result. + // + // If Go gains a native int256 type and we switch this value + // to be based on it, then we need to follow INT32-C: + // + // if (o > 0) && (v > (Int256TypeMaxIntBig - o)) { + // ... + // } else if (o < 0) && (v < (Int256TypeMinIntBig - o)) { + // ... + // } + // + res := new(big.Int) + res.Add(v.BigInt, o.BigInt) + if res.Cmp(sema.Int256TypeMinIntBig) < 0 { + return Int256Value{sema.Int256TypeMinIntBig} + } else if res.Cmp(sema.Int256TypeMaxIntBig) > 0 { + return Int256Value{sema.Int256TypeMaxIntBig} + } + return Int256Value{res} +} + func (v Int256Value) Minus(other NumberValue) NumberValue { o := other.(Int256Value) // Given that this value is backed by an arbitrary size integer, @@ -2537,6 +2901,30 @@ func (v Int256Value) Minus(other NumberValue) NumberValue { return Int256Value{res} } +func (v Int256Value) SaturatingMinus(other NumberValue) NumberValue { + o := other.(Int256Value) + // Given that this value is backed by an arbitrary size integer, + // we can just subtract and check the range of the result. + // + // If Go gains a native int256 type and we switch this value + // to be based on it, then we need to follow INT32-C: + // + // if (o > 0) && (v < (Int256TypeMinIntBig + o)) { + // ... + // } else if (o < 0) && (v > (Int256TypeMaxIntBig + o)) { + // ... + // } + // + res := new(big.Int) + res.Sub(v.BigInt, o.BigInt) + if res.Cmp(sema.Int256TypeMinIntBig) < 0 { + return Int256Value{sema.Int256TypeMinIntBig} + } else if res.Cmp(sema.Int256TypeMaxIntBig) > 0 { + return Int256Value{sema.Int256TypeMaxIntBig} + } + return Int256Value{res} +} + func (v Int256Value) Mod(other NumberValue) NumberValue { o := other.(Int256Value) res := new(big.Int) @@ -2560,6 +2948,18 @@ func (v Int256Value) Mul(other NumberValue) NumberValue { return Int256Value{res} } +func (v Int256Value) SaturatingMul(other NumberValue) NumberValue { + o := other.(Int256Value) + res := new(big.Int) + res.Mul(v.BigInt, o.BigInt) + if res.Cmp(sema.Int256TypeMinIntBig) < 0 { + return Int256Value{sema.Int256TypeMinIntBig} + } else if res.Cmp(sema.Int256TypeMaxIntBig) > 0 { + return Int256Value{sema.Int256TypeMaxIntBig} + } + return Int256Value{res} +} + func (v Int256Value) Div(other NumberValue) NumberValue { o := other.(Int256Value) res := new(big.Int) @@ -2580,6 +2980,26 @@ func (v Int256Value) Div(other NumberValue) NumberValue { return Int256Value{res} } +func (v Int256Value) SaturatingDiv(other NumberValue) NumberValue { + o := other.(Int256Value) + res := new(big.Int) + // INT33-C: + // if o == 0 { + // ... + // } else if (v == Int256TypeMinIntBig) && (o == -1) { + // ... + // } + if o.BigInt.Cmp(res) == 0 { + panic(DivisionByZeroError{}) + } + res.SetInt64(-1) + if (v.BigInt.Cmp(sema.Int256TypeMinIntBig) == 0) && (o.BigInt.Cmp(res) == 0) { + return Int256Value{sema.Int256TypeMaxIntBig} + } + res.Div(v.BigInt, o.BigInt) + return Int256Value{res} +} + func (v Int256Value) Less(other NumberValue) BoolValue { cmp := v.BigInt.Cmp(other.(Int256Value).BigInt) return cmp == -1 @@ -2815,12 +3235,24 @@ func (v UIntValue) Minus(other NumberValue) NumberValue { o := other.(UIntValue) res := new(big.Int) res.Sub(v.BigInt, o.BigInt) + // INT30-C if res.Sign() < 0 { panic(UnderflowError{}) } return UIntValue{res} } +func (v UIntValue) SaturatingMinus(other NumberValue) NumberValue { + o := other.(UIntValue) + res := new(big.Int) + res.Sub(v.BigInt, o.BigInt) + // INT30-C + if res.Sign() < 0 { + return UIntValue{sema.UIntTypeMin} + } + return UIntValue{res} +} + func (v UIntValue) Mod(other NumberValue) NumberValue { o := other.(UIntValue) res := new(big.Int) @@ -3024,6 +3456,15 @@ func (v UInt8Value) Plus(other NumberValue) NumberValue { return sum } +func (v UInt8Value) SaturatingPlus(other NumberValue) NumberValue { + sum := v + other.(UInt8Value) + // INT30-C + if sum < v { + return UInt8Value(math.MaxUint8) + } + return sum +} + func (v UInt8Value) Minus(other NumberValue) NumberValue { diff := v - other.(UInt8Value) // INT30-C @@ -3033,6 +3474,15 @@ func (v UInt8Value) Minus(other NumberValue) NumberValue { return diff } +func (v UInt8Value) SaturatingMinus(other NumberValue) NumberValue { + diff := v - other.(UInt8Value) + // INT30-C + if diff > v { + return UInt8Value(0) + } + return diff +} + func (v UInt8Value) Mod(other NumberValue) NumberValue { o := other.(UInt8Value) if o == 0 { @@ -3043,12 +3493,22 @@ func (v UInt8Value) Mod(other NumberValue) NumberValue { func (v UInt8Value) Mul(other NumberValue) NumberValue { o := other.(UInt8Value) + // INT30-C if (v > 0) && (o > 0) && (v > (math.MaxUint8 / o)) { panic(OverflowError{}) } return v * o } +func (v UInt8Value) SaturatingMul(other NumberValue) NumberValue { + o := other.(UInt8Value) + // INT30-C + if (v > 0) && (o > 0) && (v > (math.MaxUint8 / o)) { + return UInt8Value(math.MaxUint8) + } + return v * o +} + func (v UInt8Value) Div(other NumberValue) NumberValue { o := other.(UInt8Value) if o == 0 { @@ -3231,6 +3691,15 @@ func (v UInt16Value) Plus(other NumberValue) NumberValue { return sum } +func (v UInt16Value) SaturatingPlus(other NumberValue) NumberValue { + sum := v + other.(UInt16Value) + // INT30-C + if sum < v { + return UInt16Value(math.MaxUint16) + } + return sum +} + func (v UInt16Value) Minus(other NumberValue) NumberValue { diff := v - other.(UInt16Value) // INT30-C @@ -3240,6 +3709,15 @@ func (v UInt16Value) Minus(other NumberValue) NumberValue { return diff } +func (v UInt16Value) SaturatingMinus(other NumberValue) NumberValue { + diff := v - other.(UInt16Value) + // INT30-C + if diff > v { + return UInt16Value(0) + } + return diff +} + func (v UInt16Value) Mod(other NumberValue) NumberValue { o := other.(UInt16Value) if o == 0 { @@ -3250,12 +3728,22 @@ func (v UInt16Value) Mod(other NumberValue) NumberValue { func (v UInt16Value) Mul(other NumberValue) NumberValue { o := other.(UInt16Value) + // INT30-C if (v > 0) && (o > 0) && (v > (math.MaxUint16 / o)) { panic(OverflowError{}) } return v * o } +func (v UInt16Value) SaturatingMul(other NumberValue) NumberValue { + o := other.(UInt16Value) + // INT30-C + if (v > 0) && (o > 0) && (v > (math.MaxUint16 / o)) { + return UInt16Value(math.MaxUint16) + } + return v * o +} + func (v UInt16Value) Div(other NumberValue) NumberValue { o := other.(UInt16Value) if o == 0 { @@ -3442,6 +3930,15 @@ func (v UInt32Value) Plus(other NumberValue) NumberValue { return sum } +func (v UInt32Value) SaturatingPlus(other NumberValue) NumberValue { + sum := v + other.(UInt32Value) + // INT30-C + if sum < v { + return UInt32Value(math.MaxUint32) + } + return sum +} + func (v UInt32Value) Minus(other NumberValue) NumberValue { diff := v - other.(UInt32Value) // INT30-C @@ -3451,6 +3948,15 @@ func (v UInt32Value) Minus(other NumberValue) NumberValue { return diff } +func (v UInt32Value) SaturatingMinus(other NumberValue) NumberValue { + diff := v - other.(UInt32Value) + // INT30-C + if diff > v { + return UInt32Value(0) + } + return diff +} + func (v UInt32Value) Mod(other NumberValue) NumberValue { o := other.(UInt32Value) if o == 0 { @@ -3467,6 +3973,15 @@ func (v UInt32Value) Mul(other NumberValue) NumberValue { return v * o } +func (v UInt32Value) SaturatingMul(other NumberValue) NumberValue { + o := other.(UInt32Value) + // INT30-C + if (v > 0) && (o > 0) && (v > (math.MaxUint32 / o)) { + return UInt32Value(math.MaxUint32) + } + return v * o +} + func (v UInt32Value) Div(other NumberValue) NumberValue { o := other.(UInt32Value) if o == 0 { @@ -3658,6 +4173,15 @@ func (v UInt64Value) Plus(other NumberValue) NumberValue { return UInt64Value(safeAddUint64(uint64(v), uint64(o))) } +func (v UInt64Value) SaturatingPlus(other NumberValue) NumberValue { + sum := v + other.(UInt64Value) + // INT30-C + if sum < v { + return UInt64Value(math.MaxUint64) + } + return sum +} + func (v UInt64Value) Minus(other NumberValue) NumberValue { diff := v - other.(UInt64Value) // INT30-C @@ -3667,6 +4191,15 @@ func (v UInt64Value) Minus(other NumberValue) NumberValue { return diff } +func (v UInt64Value) SaturatingMinus(other NumberValue) NumberValue { + diff := v - other.(UInt64Value) + // INT30-C + if diff > v { + return UInt64Value(0) + } + return diff +} + func (v UInt64Value) Mod(other NumberValue) NumberValue { o := other.(UInt64Value) if o == 0 { @@ -3683,6 +4216,15 @@ func (v UInt64Value) Mul(other NumberValue) NumberValue { return v * o } +func (v UInt64Value) SaturatingMul(other NumberValue) NumberValue { + o := other.(UInt64Value) + // INT30-C + if (v > 0) && (o > 0) && (v > (math.MaxUint64 / o)) { + return UInt64Value(math.MaxUint64) + } + return v * o +} + func (v UInt64Value) Div(other NumberValue) NumberValue { o := other.(UInt64Value) if o == 0 { @@ -3892,6 +4434,25 @@ func (v UInt128Value) Plus(other NumberValue) NumberValue { return UInt128Value{sum} } +func (v UInt128Value) SaturatingPlus(other NumberValue) NumberValue { + sum := new(big.Int) + sum.Add(v.BigInt, other.(UInt128Value).BigInt) + // Given that this value is backed by an arbitrary size integer, + // we can just add and check the range of the result. + // + // If Go gains a native uint128 type and we switch this value + // to be based on it, then we need to follow INT30-C: + // + // if sum < v { + // ... + // } + // + if sum.Cmp(sema.UInt128TypeMaxIntBig) > 0 { + return UInt128Value{sema.UInt128TypeMaxIntBig} + } + return UInt128Value{sum} +} + func (v UInt128Value) Minus(other NumberValue) NumberValue { diff := new(big.Int) diff.Sub(v.BigInt, other.(UInt128Value).BigInt) @@ -3911,6 +4472,25 @@ func (v UInt128Value) Minus(other NumberValue) NumberValue { return UInt128Value{diff} } +func (v UInt128Value) SaturatingMinus(other NumberValue) NumberValue { + diff := new(big.Int) + diff.Sub(v.BigInt, other.(UInt128Value).BigInt) + // Given that this value is backed by an arbitrary size integer, + // we can just subtract and check the range of the result. + // + // If Go gains a native uint128 type and we switch this value + // to be based on it, then we need to follow INT30-C: + // + // if diff > v { + // ... + // } + // + if diff.Cmp(sema.UInt128TypeMinIntBig) < 0 { + return UInt128Value{sema.UInt128TypeMinIntBig} + } + return UInt128Value{diff} +} + func (v UInt128Value) Mod(other NumberValue) NumberValue { o := other.(UInt128Value) res := new(big.Int) @@ -3931,6 +4511,16 @@ func (v UInt128Value) Mul(other NumberValue) NumberValue { return UInt128Value{res} } +func (v UInt128Value) SaturatingMul(other NumberValue) NumberValue { + o := other.(UInt128Value) + res := new(big.Int) + res.Mul(v.BigInt, o.BigInt) + if res.Cmp(sema.UInt128TypeMaxIntBig) > 0 { + return UInt128Value{sema.UInt128TypeMaxIntBig} + } + return UInt128Value{res} +} + func (v UInt128Value) Div(other NumberValue) NumberValue { o := other.(UInt128Value) res := new(big.Int) @@ -4162,6 +4752,25 @@ func (v UInt256Value) Plus(other NumberValue) NumberValue { return UInt256Value{sum} } +func (v UInt256Value) SaturatingPlus(other NumberValue) NumberValue { + sum := new(big.Int) + sum.Add(v.BigInt, other.(UInt256Value).BigInt) + // Given that this value is backed by an arbitrary size integer, + // we can just add and check the range of the result. + // + // If Go gains a native uint256 type and we switch this value + // to be based on it, then we need to follow INT30-C: + // + // if sum < v { + // ... + // } + // + if sum.Cmp(sema.UInt256TypeMaxIntBig) > 0 { + return UInt256Value{sema.UInt256TypeMaxIntBig} + } + return UInt256Value{sum} +} + func (v UInt256Value) Minus(other NumberValue) NumberValue { diff := new(big.Int) diff.Sub(v.BigInt, other.(UInt256Value).BigInt) @@ -4181,6 +4790,25 @@ func (v UInt256Value) Minus(other NumberValue) NumberValue { return UInt256Value{diff} } +func (v UInt256Value) SaturatingMinus(other NumberValue) NumberValue { + diff := new(big.Int) + diff.Sub(v.BigInt, other.(UInt256Value).BigInt) + // Given that this value is backed by an arbitrary size integer, + // we can just subtract and check the range of the result. + // + // If Go gains a native uint256 type and we switch this value + // to be based on it, then we need to follow INT30-C: + // + // if diff > v { + // ... + // } + // + if diff.Cmp(sema.UInt256TypeMinIntBig) < 0 { + return UInt256Value{sema.UInt256TypeMinIntBig} + } + return UInt256Value{diff} +} + func (v UInt256Value) Mod(other NumberValue) NumberValue { o := other.(UInt256Value) res := new(big.Int) @@ -4201,6 +4829,16 @@ func (v UInt256Value) Mul(other NumberValue) NumberValue { return UInt256Value{res} } +func (v UInt256Value) SaturatingMul(other NumberValue) NumberValue { + o := other.(UInt256Value) + res := new(big.Int) + res.Mul(v.BigInt, o.BigInt) + if res.Cmp(sema.UInt256TypeMaxIntBig) > 0 { + return UInt256Value{sema.UInt256TypeMaxIntBig} + } + return UInt256Value{res} +} + func (v UInt256Value) Div(other NumberValue) NumberValue { o := other.(UInt256Value) res := new(big.Int) @@ -5107,6 +5745,17 @@ func (v Fix64Value) Plus(other NumberValue) NumberValue { return Fix64Value(safeAddInt64(int64(v), int64(o))) } +func (v Fix64Value) SaturatingPlus(other NumberValue) NumberValue { + o := other.(Fix64Value) + // INT32-C + if (o > 0) && (v > (math.MaxInt64 - o)) { + return Fix64Value(math.MaxInt64) + } else if (o < 0) && (v < (math.MinInt64 - o)) { + return Fix64Value(math.MinInt64) + } + return v + o +} + func (v Fix64Value) Minus(other NumberValue) NumberValue { o := other.(Fix64Value) // INT32-C @@ -5118,6 +5767,20 @@ func (v Fix64Value) Minus(other NumberValue) NumberValue { return v - o } +func (v Fix64Value) SaturatingMinus(other NumberValue) NumberValue { + o := other.(Fix64Value) + // INT32-C + if (o > 0) && (v < (math.MinInt64 + o)) { + return Fix64Value(math.MaxInt64) + } else if (o < 0) && (v > (math.MaxInt64 + o)) { + return Fix64Value(math.MinInt64) + } + return v - o +} + +var minInt64Big = big.NewInt(math.MinInt64) +var maxInt64Big = big.NewInt(math.MaxInt64) + func (v Fix64Value) Mul(other NumberValue) NumberValue { o := other.(Fix64Value) @@ -5136,6 +5799,24 @@ func (v Fix64Value) Mul(other NumberValue) NumberValue { return Fix64Value(result.Int64()) } +func (v Fix64Value) SaturatingMul(other NumberValue) NumberValue { + o := other.(Fix64Value) + + a := new(big.Int).SetInt64(int64(v)) + b := new(big.Int).SetInt64(int64(o)) + + result := new(big.Int).Mul(a, b) + result.Div(result, sema.Fix64FactorBig) + + if result.Cmp(minInt64Big) < 0 { + return Fix64Value(math.MinInt64) + } else if result.Cmp(maxInt64Big) > 0 { + return Fix64Value(math.MaxInt64) + } + + return Fix64Value(result.Int64()) +} + func (v Fix64Value) Div(other NumberValue) NumberValue { o := other.(Fix64Value) @@ -5154,6 +5835,24 @@ func (v Fix64Value) Div(other NumberValue) NumberValue { return Fix64Value(result.Int64()) } +func (v Fix64Value) SaturatingDiv(other NumberValue) NumberValue { + o := other.(Fix64Value) + + a := new(big.Int).SetInt64(int64(v)) + b := new(big.Int).SetInt64(int64(o)) + + result := new(big.Int).Mul(a, sema.Fix64FactorBig) + result.Div(result, b) + + if result.Cmp(minInt64Big) < 0 { + return Fix64Value(math.MinInt64) + } else if result.Cmp(maxInt64Big) > 0 { + return Fix64Value(math.MaxInt64) + } + + return Fix64Value(result.Int64()) +} + func (v Fix64Value) Mod(other NumberValue) NumberValue { o := other.(Fix64Value) // v - int(v/o) * o @@ -5327,6 +6026,16 @@ func (v UFix64Value) Plus(other NumberValue) NumberValue { return UFix64Value(safeAddUint64(uint64(v), uint64(o))) } +func (v UFix64Value) SaturatingPlus(other NumberValue) NumberValue { + o := other.(UFix64Value) + sum := v + o + // INT30-C + if sum < v { + return UFix64Value(math.MaxUint64) + } + return sum +} + func (v UFix64Value) Minus(other NumberValue) NumberValue { diff := v - other.(UFix64Value) // INT30-C @@ -5336,6 +6045,15 @@ func (v UFix64Value) Minus(other NumberValue) NumberValue { return diff } +func (v UFix64Value) SaturatingMinus(other NumberValue) NumberValue { + diff := v - other.(UFix64Value) + // INT30-C + if diff > v { + return UFix64Value(0) + } + return diff +} + func (v UFix64Value) Mul(other NumberValue) NumberValue { o := other.(UFix64Value) @@ -5352,22 +6070,34 @@ func (v UFix64Value) Mul(other NumberValue) NumberValue { return UFix64Value(result.Uint64()) } -func (v UFix64Value) Div(other NumberValue) NumberValue { +func (v UFix64Value) SaturatingMul(other NumberValue) NumberValue { o := other.(UFix64Value) a := new(big.Int).SetUint64(uint64(v)) b := new(big.Int).SetUint64(uint64(o)) - result := new(big.Int).Mul(a, sema.Fix64FactorBig) - result.Div(result, b) + result := new(big.Int).Mul(a, b) + result.Div(result, sema.Fix64FactorBig) if !result.IsUint64() { - panic(OverflowError{}) + return UFix64Value(math.MaxUint64) } return UFix64Value(result.Uint64()) } +func (v UFix64Value) Div(other NumberValue) NumberValue { + o := other.(UFix64Value) + + a := new(big.Int).SetUint64(uint64(v)) + b := new(big.Int).SetUint64(uint64(o)) + + result := new(big.Int).Mul(a, sema.Fix64FactorBig) + result.Div(result, b) + + return UFix64Value(result.Uint64()) +} + func (v UFix64Value) Mod(other NumberValue) NumberValue { o := other.(UFix64Value) // v - int(v/o) * o From 457f97e04abb15af6137dca16905a95bb7c29458 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Wed, 14 Apr 2021 18:13:40 -0700 Subject: [PATCH 03/11] declare saturating arithmetic functions for integer and fixed-point types --- runtime/sema/type.go | 287 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 261 insertions(+), 26 deletions(-) diff --git a/runtime/sema/type.go b/runtime/sema/type.go index 753db9bb8a..996a4c1daa 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -722,9 +722,15 @@ type FractionalRangedType interface { // and non-fractional ranged types. // type NumericType struct { - name string - minInt *big.Int - maxInt *big.Int + name string + minInt *big.Int + maxInt *big.Int + saturatingAdd bool + saturatingSubtract bool + saturatingMultiply bool + saturatingDivide bool + memberResolvers map[string]MemberResolver + memberResolversOnce sync.Once } var _ IntegerRangedType = &NumericType{} @@ -739,6 +745,26 @@ func (t *NumericType) WithIntRange(min *big.Int, max *big.Int) *NumericType { return t } +func (t *NumericType) WithSaturatingAdd() *NumericType { + t.saturatingAdd = true + return t +} + +func (t *NumericType) WithSaturatingSubtract() *NumericType { + t.saturatingSubtract = true + return t +} + +func (t *NumericType) WithSaturatingMultiply() *NumericType { + t.saturatingMultiply = true + return t +} + +func (t *NumericType) WithSaturatingDivide() *NumericType { + t.saturatingDivide = true + return t +} + func (*NumericType) IsType() {} func (t *NumericType) String() string { @@ -809,18 +835,100 @@ func (t *NumericType) Resolve(_ *TypeParameterTypeOrderedMap) Type { } func (t *NumericType) GetMembers() map[string]MemberResolver { - return withBuiltinMembers(t, nil) + t.initializeMemberResolvers() + return t.memberResolvers +} + +const NumericTypeSaturatingAddFunctionName = "saturatingAdd" +const numericTypeSaturatingAddFunctionDocString = ` +self + other, saturating at the numeric bounds instead of overflowing. +` + +const NumericTypeSaturatingSubtractFunctionName = "saturatingSubtract" +const numericTypeSaturatingSubtractFunctionDocString = ` +self - other, saturating at the numeric bounds instead of overflowing. +` +const NumericTypeSaturatingMultiplyFunctionName = "saturatingMultiply" +const numericTypeSaturatingMultiplyFunctionDocString = ` +self * other, saturating at the numeric bounds instead of overflowing. +` + +const NumericTypeSaturatingDivideFunctionName = "saturatingDivide" +const numericTypeSaturatingDivideFunctionDocString = ` +self / other, saturating at the numeric bounds instead of overflowing. +` + +func (t *NumericType) initializeMemberResolvers() { + t.memberResolversOnce.Do(func() { + members := map[string]MemberResolver{} + + arithmeticFunctionType := &FunctionType{ + Parameters: []*Parameter{ + { + Label: ArgumentLabelNotRequired, + Identifier: "other", + TypeAnnotation: NewTypeAnnotation(t), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation(t), + } + + addArithmeticFunction := func(name string, docString string) { + members[name] = MemberResolver{ + Kind: common.DeclarationKindFunction, + Resolve: func(identifier string, targetRange ast.Range, report func(error)) *Member { + return NewPublicFunctionMember(t, name, arithmeticFunctionType, docString) + }, + } + } + + if t.saturatingAdd { + addArithmeticFunction( + NumericTypeSaturatingAddFunctionName, + numericTypeSaturatingAddFunctionDocString, + ) + } + + if t.saturatingSubtract { + addArithmeticFunction( + NumericTypeSaturatingSubtractFunctionName, + numericTypeSaturatingSubtractFunctionDocString, + ) + } + + if t.saturatingMultiply { + addArithmeticFunction( + NumericTypeSaturatingMultiplyFunctionName, + numericTypeSaturatingMultiplyFunctionDocString, + ) + } + + if t.saturatingDivide { + addArithmeticFunction( + NumericTypeSaturatingDivideFunctionName, + numericTypeSaturatingDivideFunctionDocString, + ) + } + + t.memberResolvers = withBuiltinMembers(t, members) + }) } // FixedPointNumericType represents all the types in the fixed-point range. // type FixedPointNumericType struct { - name string - scale uint - minInt *big.Int - maxInt *big.Int - minFractional *big.Int - maxFractional *big.Int + name string + scale uint + minInt *big.Int + maxInt *big.Int + minFractional *big.Int + maxFractional *big.Int + saturatingAdd bool + saturatingSubtract bool + saturatingMultiply bool + saturatingDivide bool + memberResolvers map[string]MemberResolver + memberResolversOnce sync.Once } var _ FractionalRangedType = &FixedPointNumericType{} @@ -852,6 +960,26 @@ func (t *FixedPointNumericType) WithScale(scale uint) *FixedPointNumericType { return t } +func (t *FixedPointNumericType) WithSaturatingAdd() *FixedPointNumericType { + t.saturatingAdd = true + return t +} + +func (t *FixedPointNumericType) WithSaturatingSubtract() *FixedPointNumericType { + t.saturatingSubtract = true + return t +} + +func (t *FixedPointNumericType) WithSaturatingMultiply() *FixedPointNumericType { + t.saturatingMultiply = true + return t +} + +func (t *FixedPointNumericType) WithSaturatingDivide() *FixedPointNumericType { + t.saturatingDivide = true + return t +} + func (*FixedPointNumericType) IsType() {} func (t *FixedPointNumericType) String() string { @@ -934,7 +1062,64 @@ func (t *FixedPointNumericType) Resolve(_ *TypeParameterTypeOrderedMap) Type { } func (t *FixedPointNumericType) GetMembers() map[string]MemberResolver { - return withBuiltinMembers(t, nil) + t.initializeMemberResolvers() + return t.memberResolvers +} + +func (t *FixedPointNumericType) initializeMemberResolvers() { + t.memberResolversOnce.Do(func() { + members := map[string]MemberResolver{} + + arithmeticFunctionType := &FunctionType{ + Parameters: []*Parameter{ + { + Label: ArgumentLabelNotRequired, + Identifier: "other", + TypeAnnotation: NewTypeAnnotation(t), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation(t), + } + + addArithmeticFunction := func(name string, docString string) { + members[name] = MemberResolver{ + Kind: common.DeclarationKindFunction, + Resolve: func(identifier string, targetRange ast.Range, report func(error)) *Member { + return NewPublicFunctionMember(t, name, arithmeticFunctionType, docString) + }, + } + } + + if t.saturatingAdd { + addArithmeticFunction( + NumericTypeSaturatingAddFunctionName, + numericTypeSaturatingAddFunctionDocString, + ) + } + + if t.saturatingSubtract { + addArithmeticFunction( + NumericTypeSaturatingSubtractFunctionName, + numericTypeSaturatingSubtractFunctionDocString, + ) + } + + if t.saturatingMultiply { + addArithmeticFunction( + NumericTypeSaturatingMultiplyFunctionName, + numericTypeSaturatingMultiplyFunctionDocString, + ) + } + + if t.saturatingDivide { + addArithmeticFunction( + NumericTypeSaturatingDivideFunctionName, + numericTypeSaturatingDivideFunctionDocString, + ) + } + + t.memberResolvers = withBuiltinMembers(t, members) + }) } // Numeric types @@ -958,61 +1143,104 @@ var ( // Int8Type represents the 8-bit signed integer type `Int8` Int8Type = NewNumericType(Int8TypeName). - WithIntRange(Int8TypeMinInt, Int8TypeMaxInt) + WithIntRange(Int8TypeMinInt, Int8TypeMaxInt). + WithSaturatingAdd(). + WithSaturatingSubtract(). + WithSaturatingMultiply(). + WithSaturatingDivide() // Int16Type represents the 16-bit signed integer type `Int16` Int16Type = NewNumericType(Int16TypeName). - WithIntRange(Int16TypeMinInt, Int16TypeMaxInt) + WithIntRange(Int16TypeMinInt, Int16TypeMaxInt). + WithSaturatingAdd(). + WithSaturatingSubtract(). + WithSaturatingMultiply(). + WithSaturatingDivide() // Int32Type represents the 32-bit signed integer type `Int32` Int32Type = NewNumericType(Int32TypeName). - WithIntRange(Int32TypeMinInt, Int32TypeMaxInt) + WithIntRange(Int32TypeMinInt, Int32TypeMaxInt). + WithSaturatingAdd(). + WithSaturatingSubtract(). + WithSaturatingMultiply(). + WithSaturatingDivide() // Int64Type represents the 64-bit signed integer type `Int64` Int64Type = NewNumericType(Int64TypeName). - WithIntRange(Int64TypeMinInt, Int64TypeMaxInt) + WithIntRange(Int64TypeMinInt, Int64TypeMaxInt). + WithSaturatingAdd(). + WithSaturatingSubtract(). + WithSaturatingMultiply(). + WithSaturatingDivide() // Int128Type represents the 128-bit signed integer type `Int128` Int128Type = NewNumericType(Int128TypeName). - WithIntRange(Int128TypeMinIntBig, Int128TypeMaxIntBig) + WithIntRange(Int128TypeMinIntBig, Int128TypeMaxIntBig). + WithSaturatingAdd(). + WithSaturatingSubtract(). + WithSaturatingMultiply(). + WithSaturatingDivide() // Int256Type represents the 256-bit signed integer type `Int256` Int256Type = NewNumericType(Int256TypeName). - WithIntRange(Int256TypeMinIntBig, Int256TypeMaxIntBig) + WithIntRange(Int256TypeMinIntBig, Int256TypeMaxIntBig). + WithSaturatingAdd(). + WithSaturatingSubtract(). + WithSaturatingMultiply(). + WithSaturatingDivide() // UIntType represents the arbitrary-precision unsigned integer type `UInt` UIntType = NewNumericType(UIntTypeName). - WithIntRange(UIntTypeMin, nil) + WithIntRange(UIntTypeMin, nil). + WithSaturatingSubtract() // UInt8Type represents the 8-bit unsigned integer type `UInt8` // which checks for overflow and underflow UInt8Type = NewNumericType(UInt8TypeName). - WithIntRange(UInt8TypeMinInt, UInt8TypeMaxInt) + WithIntRange(UInt8TypeMinInt, UInt8TypeMaxInt). + WithSaturatingAdd(). + WithSaturatingSubtract(). + WithSaturatingMultiply() // UInt16Type represents the 16-bit unsigned integer type `UInt16` // which checks for overflow and underflow UInt16Type = NewNumericType(UInt16TypeName). - WithIntRange(UInt16TypeMinInt, UInt16TypeMaxInt) + WithIntRange(UInt16TypeMinInt, UInt16TypeMaxInt). + WithSaturatingAdd(). + WithSaturatingSubtract(). + WithSaturatingMultiply() // UInt32Type represents the 32-bit unsigned integer type `UInt32` // which checks for overflow and underflow UInt32Type = NewNumericType(UInt32TypeName). - WithIntRange(UInt32TypeMinInt, UInt32TypeMaxInt) + WithIntRange(UInt32TypeMinInt, UInt32TypeMaxInt). + WithSaturatingAdd(). + WithSaturatingSubtract(). + WithSaturatingMultiply() // UInt64Type represents the 64-bit unsigned integer type `UInt64` // which checks for overflow and underflow UInt64Type = NewNumericType(UInt64TypeName). - WithIntRange(UInt64TypeMinInt, UInt64TypeMaxInt) + WithIntRange(UInt64TypeMinInt, UInt64TypeMaxInt). + WithSaturatingAdd(). + WithSaturatingSubtract(). + WithSaturatingMultiply() // UInt128Type represents the 128-bit unsigned integer type `UInt128` // which checks for overflow and underflow UInt128Type = NewNumericType(UInt128TypeName). - WithIntRange(UInt128TypeMinIntBig, UInt128TypeMaxIntBig) + WithIntRange(UInt128TypeMinIntBig, UInt128TypeMaxIntBig). + WithSaturatingAdd(). + WithSaturatingSubtract(). + WithSaturatingMultiply() // UInt256Type represents the 256-bit unsigned integer type `UInt256` // which checks for overflow and underflow UInt256Type = NewNumericType(UInt256TypeName). - WithIntRange(UInt256TypeMinIntBig, UInt256TypeMaxIntBig) + WithIntRange(UInt256TypeMinIntBig, UInt256TypeMaxIntBig). + WithSaturatingAdd(). + WithSaturatingSubtract(). + WithSaturatingMultiply() // Word8Type represents the 8-bit unsigned integer type `Word8` // which does NOT check for overflow and underflow @@ -1045,14 +1273,21 @@ var ( Fix64Type = NewFixedPointNumericType(Fix64TypeName). WithIntRange(Fix64TypeMinIntBig, Fix64TypeMaxIntBig). WithFractionalRange(Fix64TypeMinFractionalBig, Fix64TypeMaxFractionalBig). - WithScale(Fix64Scale) + WithScale(Fix64Scale). + WithSaturatingAdd(). + WithSaturatingSubtract(). + WithSaturatingMultiply(). + WithSaturatingDivide() // UFix64Type represents the 64-bit unsigned decimal fixed-point type `UFix64` // which has a scale of 1E9, and checks for overflow and underflow UFix64Type = NewFixedPointNumericType(UFix64TypeName). WithIntRange(UFix64TypeMinIntBig, UFix64TypeMaxIntBig). WithFractionalRange(UFix64TypeMinFractionalBig, UFix64TypeMaxFractionalBig). - WithScale(Fix64Scale) + WithScale(Fix64Scale). + WithSaturatingAdd(). + WithSaturatingSubtract(). + WithSaturatingMultiply() ) // Numeric type ranges From b88c7630e0b1912579af4b176d4f586880f6e271 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Wed, 14 Apr 2021 18:31:47 -0700 Subject: [PATCH 04/11] implement saturating arithmetic functions for values --- runtime/interpreter/value.go | 400 +++++++++++++++++++++++++++++++++++ 1 file changed, 400 insertions(+) diff --git a/runtime/interpreter/value.go b/runtime/interpreter/value.go index 31e463897f..64d3f3d9c0 100644 --- a/runtime/interpreter/value.go +++ b/runtime/interpreter/value.go @@ -1419,6 +1419,38 @@ func (v Int8Value) GetMember(_ *Interpreter, _ func() LocationRange, name string return ByteSliceToByteArrayValue(v.ToBigEndianBytes()) }, ) + + case sema.NumericTypeSaturatingAddFunctionName: + return NewHostFunctionValue( + func(invocation Invocation) Value { + other := invocation.Arguments[0].(NumberValue) + return v.SaturatingPlus(other) + }, + ) + + case sema.NumericTypeSaturatingSubtractFunctionName: + return NewHostFunctionValue( + func(invocation Invocation) Value { + other := invocation.Arguments[0].(NumberValue) + return v.SaturatingMinus(other) + }, + ) + + case sema.NumericTypeSaturatingMultiplyFunctionName: + return NewHostFunctionValue( + func(invocation Invocation) Value { + other := invocation.Arguments[0].(NumberValue) + return v.SaturatingMul(other) + }, + ) + + case sema.NumericTypeSaturatingDivideFunctionName: + return NewHostFunctionValue( + func(invocation Invocation) Value { + other := invocation.Arguments[0].(NumberValue) + return v.SaturatingDiv(other) + }, + ) } return nil @@ -1729,6 +1761,38 @@ func (v Int16Value) GetMember(_ *Interpreter, _ func() LocationRange, name strin return ByteSliceToByteArrayValue(v.ToBigEndianBytes()) }, ) + + case sema.NumericTypeSaturatingAddFunctionName: + return NewHostFunctionValue( + func(invocation Invocation) Value { + other := invocation.Arguments[0].(NumberValue) + return v.SaturatingPlus(other) + }, + ) + + case sema.NumericTypeSaturatingSubtractFunctionName: + return NewHostFunctionValue( + func(invocation Invocation) Value { + other := invocation.Arguments[0].(NumberValue) + return v.SaturatingMinus(other) + }, + ) + + case sema.NumericTypeSaturatingMultiplyFunctionName: + return NewHostFunctionValue( + func(invocation Invocation) Value { + other := invocation.Arguments[0].(NumberValue) + return v.SaturatingMul(other) + }, + ) + + case sema.NumericTypeSaturatingDivideFunctionName: + return NewHostFunctionValue( + func(invocation Invocation) Value { + other := invocation.Arguments[0].(NumberValue) + return v.SaturatingDiv(other) + }, + ) } return nil @@ -2041,6 +2105,38 @@ func (v Int32Value) GetMember(_ *Interpreter, _ func() LocationRange, name strin return ByteSliceToByteArrayValue(v.ToBigEndianBytes()) }, ) + + case sema.NumericTypeSaturatingAddFunctionName: + return NewHostFunctionValue( + func(invocation Invocation) Value { + other := invocation.Arguments[0].(NumberValue) + return v.SaturatingPlus(other) + }, + ) + + case sema.NumericTypeSaturatingSubtractFunctionName: + return NewHostFunctionValue( + func(invocation Invocation) Value { + other := invocation.Arguments[0].(NumberValue) + return v.SaturatingMinus(other) + }, + ) + + case sema.NumericTypeSaturatingMultiplyFunctionName: + return NewHostFunctionValue( + func(invocation Invocation) Value { + other := invocation.Arguments[0].(NumberValue) + return v.SaturatingMul(other) + }, + ) + + case sema.NumericTypeSaturatingDivideFunctionName: + return NewHostFunctionValue( + func(invocation Invocation) Value { + other := invocation.Arguments[0].(NumberValue) + return v.SaturatingDiv(other) + }, + ) } return nil @@ -2352,6 +2448,38 @@ func (v Int64Value) GetMember(_ *Interpreter, _ func() LocationRange, name strin return ByteSliceToByteArrayValue(v.ToBigEndianBytes()) }, ) + + case sema.NumericTypeSaturatingAddFunctionName: + return NewHostFunctionValue( + func(invocation Invocation) Value { + other := invocation.Arguments[0].(NumberValue) + return v.SaturatingPlus(other) + }, + ) + + case sema.NumericTypeSaturatingSubtractFunctionName: + return NewHostFunctionValue( + func(invocation Invocation) Value { + other := invocation.Arguments[0].(NumberValue) + return v.SaturatingMinus(other) + }, + ) + + case sema.NumericTypeSaturatingMultiplyFunctionName: + return NewHostFunctionValue( + func(invocation Invocation) Value { + other := invocation.Arguments[0].(NumberValue) + return v.SaturatingMul(other) + }, + ) + + case sema.NumericTypeSaturatingDivideFunctionName: + return NewHostFunctionValue( + func(invocation Invocation) Value { + other := invocation.Arguments[0].(NumberValue) + return v.SaturatingDiv(other) + }, + ) } return nil @@ -2734,6 +2862,38 @@ func (v Int128Value) GetMember(_ *Interpreter, _ func() LocationRange, name stri return ByteSliceToByteArrayValue(v.ToBigEndianBytes()) }, ) + + case sema.NumericTypeSaturatingAddFunctionName: + return NewHostFunctionValue( + func(invocation Invocation) Value { + other := invocation.Arguments[0].(NumberValue) + return v.SaturatingPlus(other) + }, + ) + + case sema.NumericTypeSaturatingSubtractFunctionName: + return NewHostFunctionValue( + func(invocation Invocation) Value { + other := invocation.Arguments[0].(NumberValue) + return v.SaturatingMinus(other) + }, + ) + + case sema.NumericTypeSaturatingMultiplyFunctionName: + return NewHostFunctionValue( + func(invocation Invocation) Value { + other := invocation.Arguments[0].(NumberValue) + return v.SaturatingMul(other) + }, + ) + + case sema.NumericTypeSaturatingDivideFunctionName: + return NewHostFunctionValue( + func(invocation Invocation) Value { + other := invocation.Arguments[0].(NumberValue) + return v.SaturatingDiv(other) + }, + ) } return nil @@ -3115,6 +3275,38 @@ func (v Int256Value) GetMember(_ *Interpreter, _ func() LocationRange, name stri return ByteSliceToByteArrayValue(v.ToBigEndianBytes()) }, ) + + case sema.NumericTypeSaturatingAddFunctionName: + return NewHostFunctionValue( + func(invocation Invocation) Value { + other := invocation.Arguments[0].(NumberValue) + return v.SaturatingPlus(other) + }, + ) + + case sema.NumericTypeSaturatingSubtractFunctionName: + return NewHostFunctionValue( + func(invocation Invocation) Value { + other := invocation.Arguments[0].(NumberValue) + return v.SaturatingMinus(other) + }, + ) + + case sema.NumericTypeSaturatingMultiplyFunctionName: + return NewHostFunctionValue( + func(invocation Invocation) Value { + other := invocation.Arguments[0].(NumberValue) + return v.SaturatingMul(other) + }, + ) + + case sema.NumericTypeSaturatingDivideFunctionName: + return NewHostFunctionValue( + func(invocation Invocation) Value { + other := invocation.Arguments[0].(NumberValue) + return v.SaturatingDiv(other) + }, + ) } return nil @@ -3374,6 +3566,14 @@ func (v UIntValue) GetMember(_ *Interpreter, _ func() LocationRange, name string return ByteSliceToByteArrayValue(v.ToBigEndianBytes()) }, ) + + case sema.NumericTypeSaturatingSubtractFunctionName: + return NewHostFunctionValue( + func(invocation Invocation) Value { + other := invocation.Arguments[0].(NumberValue) + return v.SaturatingMinus(other) + }, + ) } return nil @@ -3611,6 +3811,30 @@ func (v UInt8Value) GetMember(_ *Interpreter, _ func() LocationRange, name strin return ByteSliceToByteArrayValue(v.ToBigEndianBytes()) }, ) + + case sema.NumericTypeSaturatingAddFunctionName: + return NewHostFunctionValue( + func(invocation Invocation) Value { + other := invocation.Arguments[0].(NumberValue) + return v.SaturatingPlus(other) + }, + ) + + case sema.NumericTypeSaturatingSubtractFunctionName: + return NewHostFunctionValue( + func(invocation Invocation) Value { + other := invocation.Arguments[0].(NumberValue) + return v.SaturatingMinus(other) + }, + ) + + case sema.NumericTypeSaturatingMultiplyFunctionName: + return NewHostFunctionValue( + func(invocation Invocation) Value { + other := invocation.Arguments[0].(NumberValue) + return v.SaturatingMul(other) + }, + ) } return nil @@ -3846,6 +4070,30 @@ func (v UInt16Value) GetMember(_ *Interpreter, _ func() LocationRange, name stri return ByteSliceToByteArrayValue(v.ToBigEndianBytes()) }, ) + + case sema.NumericTypeSaturatingAddFunctionName: + return NewHostFunctionValue( + func(invocation Invocation) Value { + other := invocation.Arguments[0].(NumberValue) + return v.SaturatingPlus(other) + }, + ) + + case sema.NumericTypeSaturatingSubtractFunctionName: + return NewHostFunctionValue( + func(invocation Invocation) Value { + other := invocation.Arguments[0].(NumberValue) + return v.SaturatingMinus(other) + }, + ) + + case sema.NumericTypeSaturatingMultiplyFunctionName: + return NewHostFunctionValue( + func(invocation Invocation) Value { + other := invocation.Arguments[0].(NumberValue) + return v.SaturatingMul(other) + }, + ) } return nil @@ -4084,6 +4332,30 @@ func (v UInt32Value) GetMember(_ *Interpreter, _ func() LocationRange, name stri return ByteSliceToByteArrayValue(v.ToBigEndianBytes()) }, ) + + case sema.NumericTypeSaturatingAddFunctionName: + return NewHostFunctionValue( + func(invocation Invocation) Value { + other := invocation.Arguments[0].(NumberValue) + return v.SaturatingPlus(other) + }, + ) + + case sema.NumericTypeSaturatingSubtractFunctionName: + return NewHostFunctionValue( + func(invocation Invocation) Value { + other := invocation.Arguments[0].(NumberValue) + return v.SaturatingMinus(other) + }, + ) + + case sema.NumericTypeSaturatingMultiplyFunctionName: + return NewHostFunctionValue( + func(invocation Invocation) Value { + other := invocation.Arguments[0].(NumberValue) + return v.SaturatingMul(other) + }, + ) } return nil @@ -4325,6 +4597,30 @@ func (v UInt64Value) GetMember(_ *Interpreter, _ func() LocationRange, name stri return ByteSliceToByteArrayValue(v.ToBigEndianBytes()) }, ) + + case sema.NumericTypeSaturatingAddFunctionName: + return NewHostFunctionValue( + func(invocation Invocation) Value { + other := invocation.Arguments[0].(NumberValue) + return v.SaturatingPlus(other) + }, + ) + + case sema.NumericTypeSaturatingSubtractFunctionName: + return NewHostFunctionValue( + func(invocation Invocation) Value { + other := invocation.Arguments[0].(NumberValue) + return v.SaturatingMinus(other) + }, + ) + + case sema.NumericTypeSaturatingMultiplyFunctionName: + return NewHostFunctionValue( + func(invocation Invocation) Value { + other := invocation.Arguments[0].(NumberValue) + return v.SaturatingMul(other) + }, + ) } return nil @@ -4645,6 +4941,30 @@ func (v UInt128Value) GetMember(_ *Interpreter, _ func() LocationRange, name str return ByteSliceToByteArrayValue(v.ToBigEndianBytes()) }, ) + + case sema.NumericTypeSaturatingAddFunctionName: + return NewHostFunctionValue( + func(invocation Invocation) Value { + other := invocation.Arguments[0].(NumberValue) + return v.SaturatingPlus(other) + }, + ) + + case sema.NumericTypeSaturatingSubtractFunctionName: + return NewHostFunctionValue( + func(invocation Invocation) Value { + other := invocation.Arguments[0].(NumberValue) + return v.SaturatingMinus(other) + }, + ) + + case sema.NumericTypeSaturatingMultiplyFunctionName: + return NewHostFunctionValue( + func(invocation Invocation) Value { + other := invocation.Arguments[0].(NumberValue) + return v.SaturatingMul(other) + }, + ) } return nil @@ -4964,6 +5284,30 @@ func (v UInt256Value) GetMember(_ *Interpreter, _ func() LocationRange, name str return ByteSliceToByteArrayValue(v.ToBigEndianBytes()) }, ) + + case sema.NumericTypeSaturatingAddFunctionName: + return NewHostFunctionValue( + func(invocation Invocation) Value { + other := invocation.Arguments[0].(NumberValue) + return v.SaturatingPlus(other) + }, + ) + + case sema.NumericTypeSaturatingSubtractFunctionName: + return NewHostFunctionValue( + func(invocation Invocation) Value { + other := invocation.Arguments[0].(NumberValue) + return v.SaturatingMinus(other) + }, + ) + + case sema.NumericTypeSaturatingMultiplyFunctionName: + return NewHostFunctionValue( + func(invocation Invocation) Value { + other := invocation.Arguments[0].(NumberValue) + return v.SaturatingMul(other) + }, + ) } return nil @@ -5936,6 +6280,38 @@ func (v Fix64Value) GetMember(_ *Interpreter, _ func() LocationRange, name strin return ByteSliceToByteArrayValue(v.ToBigEndianBytes()) }, ) + + case sema.NumericTypeSaturatingAddFunctionName: + return NewHostFunctionValue( + func(invocation Invocation) Value { + other := invocation.Arguments[0].(NumberValue) + return v.SaturatingPlus(other) + }, + ) + + case sema.NumericTypeSaturatingSubtractFunctionName: + return NewHostFunctionValue( + func(invocation Invocation) Value { + other := invocation.Arguments[0].(NumberValue) + return v.SaturatingMinus(other) + }, + ) + + case sema.NumericTypeSaturatingMultiplyFunctionName: + return NewHostFunctionValue( + func(invocation Invocation) Value { + other := invocation.Arguments[0].(NumberValue) + return v.SaturatingMul(other) + }, + ) + + case sema.NumericTypeSaturatingDivideFunctionName: + return NewHostFunctionValue( + func(invocation Invocation) Value { + other := invocation.Arguments[0].(NumberValue) + return v.SaturatingDiv(other) + }, + ) } return nil @@ -6188,6 +6564,30 @@ func (v UFix64Value) GetMember(_ *Interpreter, _ func() LocationRange, name stri return ByteSliceToByteArrayValue(v.ToBigEndianBytes()) }, ) + + case sema.NumericTypeSaturatingAddFunctionName: + return NewHostFunctionValue( + func(invocation Invocation) Value { + other := invocation.Arguments[0].(NumberValue) + return v.SaturatingPlus(other) + }, + ) + + case sema.NumericTypeSaturatingSubtractFunctionName: + return NewHostFunctionValue( + func(invocation Invocation) Value { + other := invocation.Arguments[0].(NumberValue) + return v.SaturatingMinus(other) + }, + ) + + case sema.NumericTypeSaturatingMultiplyFunctionName: + return NewHostFunctionValue( + func(invocation Invocation) Value { + other := invocation.Arguments[0].(NumberValue) + return v.SaturatingMul(other) + }, + ) } return nil From ca3a29efed7874badcb1c87e0ad6c4b7c205e788 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Thu, 15 Apr 2021 07:02:05 -0700 Subject: [PATCH 05/11] add checker tests for saturating arithmetic functions --- runtime/tests/checker/operations_test.go | 104 +++++++++++++++++++++++ 1 file changed, 104 insertions(+) diff --git a/runtime/tests/checker/operations_test.go b/runtime/tests/checker/operations_test.go index 766f482e71..fcb6265578 100644 --- a/runtime/tests/checker/operations_test.go +++ b/runtime/tests/checker/operations_test.go @@ -20,6 +20,7 @@ package checker import ( "fmt" + "strings" "testing" "github.com/stretchr/testify/assert" @@ -289,6 +290,109 @@ func TestCheckIntegerBinaryOperations(t *testing.T) { } } +func TestCheckSaturatedArithmeticFunctions(t *testing.T) { + + t.Parallel() + + type testCase struct { + ty sema.Type + add, subtract, multiply, divide bool + } + + testCases := []testCase{ + { + ty: sema.IntType, + add: false, + subtract: false, + multiply: false, + divide: false, + }, + { + ty: sema.UIntType, + add: false, + subtract: true, + multiply: false, + divide: false, + }, + } + + for _, ty := range append( + sema.AllSignedIntegerTypes[:], + sema.AllSignedFixedPointTypes..., + ) { + + if ty == sema.IntType { + continue + } + + testCases = append(testCases, testCase{ + ty: ty, + add: true, + subtract: true, + multiply: true, + divide: true, + }) + } + + for _, ty := range append( + sema.AllUnsignedIntegerTypes[:], + sema.AllUnsignedFixedPointTypes..., + ) { + + if ty == sema.UIntType || strings.HasPrefix(ty.String(), "Word") { + continue + } + + testCases = append(testCases, testCase{ + ty: ty, + add: true, + subtract: true, + multiply: true, + divide: false, + }) + } + + test := func(ty sema.Type, method string, expected bool) { + + method = fmt.Sprintf("saturating%s", method) + + t.Run(fmt.Sprintf("%s %s", ty, method), func(t *testing.T) { + + _, err := ParseAndCheckWithPanic(t, + fmt.Sprintf( + ` + fun test(): %[1]s { + let a: %[1]s = panic("") + let b: %[1]s = panic("") + return a.%[2]s(b) + } + `, + ty, + method, + ), + ) + + if expected { + errs := ExpectCheckerErrors(t, err, 1) + + assert.IsType(t, &sema.UnreachableStatementError{}, errs[0]) + } else { + errs := ExpectCheckerErrors(t, err, 2) + + assert.IsType(t, &sema.UnreachableStatementError{}, errs[0]) + assert.IsType(t, &sema.NotDeclaredMemberError{}, errs[1]) + } + }) + } + + for _, testCase := range testCases { + test(testCase.ty, "Add", testCase.add) + test(testCase.ty, "Subtract", testCase.subtract) + test(testCase.ty, "Multiply", testCase.multiply) + test(testCase.ty, "Divide", testCase.divide) + } +} + func TestCheckInvalidCompositeEquality(t *testing.T) { t.Parallel() From 1820f3c20eac197bfe9dc97b5d42d2c458d88e07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Thu, 15 Apr 2021 07:20:23 -0700 Subject: [PATCH 06/11] simplify test --- runtime/tests/checker/operations_test.go | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/runtime/tests/checker/operations_test.go b/runtime/tests/checker/operations_test.go index fcb6265578..7ae9738523 100644 --- a/runtime/tests/checker/operations_test.go +++ b/runtime/tests/checker/operations_test.go @@ -361,9 +361,7 @@ func TestCheckSaturatedArithmeticFunctions(t *testing.T) { _, err := ParseAndCheckWithPanic(t, fmt.Sprintf( ` - fun test(): %[1]s { - let a: %[1]s = panic("") - let b: %[1]s = panic("") + fun test(a: %[1]s, b: %[1]s): %[1]s { return a.%[2]s(b) } `, @@ -373,14 +371,11 @@ func TestCheckSaturatedArithmeticFunctions(t *testing.T) { ) if expected { - errs := ExpectCheckerErrors(t, err, 1) - - assert.IsType(t, &sema.UnreachableStatementError{}, errs[0]) + require.NoError(t, err) } else { - errs := ExpectCheckerErrors(t, err, 2) + errs := ExpectCheckerErrors(t, err, 1) - assert.IsType(t, &sema.UnreachableStatementError{}, errs[0]) - assert.IsType(t, &sema.NotDeclaredMemberError{}, errs[1]) + assert.IsType(t, &sema.NotDeclaredMemberError{}, errs[0]) } }) } From 459438bf90686018cc27603c0d77c948e872d7ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Thu, 15 Apr 2021 17:59:32 -0700 Subject: [PATCH 07/11] fix saturating subtraction --- runtime/interpreter/value.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/runtime/interpreter/value.go b/runtime/interpreter/value.go index 64d3f3d9c0..ec1645e5b1 100644 --- a/runtime/interpreter/value.go +++ b/runtime/interpreter/value.go @@ -1223,9 +1223,9 @@ func (v Int8Value) SaturatingMinus(other NumberValue) NumberValue { o := other.(Int8Value) // INT32-C if (o > 0) && (v < (math.MinInt8 + o)) { - return Int8Value(math.MaxInt8) - } else if (o < 0) && (v > (math.MaxInt8 + o)) { return Int8Value(math.MinInt8) + } else if (o < 0) && (v > (math.MaxInt8 + o)) { + return Int8Value(math.MaxInt8) } return v - o } @@ -1565,9 +1565,9 @@ func (v Int16Value) SaturatingMinus(other NumberValue) NumberValue { o := other.(Int16Value) // INT32-C if (o > 0) && (v < (math.MinInt16 + o)) { - return Int16Value(math.MaxInt16) - } else if (o < 0) && (v > (math.MaxInt16 + o)) { return Int16Value(math.MinInt16) + } else if (o < 0) && (v > (math.MaxInt16 + o)) { + return Int16Value(math.MaxInt16) } return v - o } @@ -1909,9 +1909,9 @@ func (v Int32Value) SaturatingMinus(other NumberValue) NumberValue { o := other.(Int32Value) // INT32-C if (o > 0) && (v < (math.MinInt32 + o)) { - return Int32Value(math.MaxInt32) - } else if (o < 0) && (v > (math.MaxInt32 + o)) { return Int32Value(math.MinInt32) + } else if (o < 0) && (v > (math.MaxInt32 + o)) { + return Int32Value(math.MaxInt32) } return v - o } @@ -2257,9 +2257,9 @@ func (v Int64Value) SaturatingMinus(other NumberValue) NumberValue { o := other.(Int64Value) // INT32-C if (o > 0) && (v < (math.MinInt64 + o)) { - return Int64Value(math.MaxInt64) - } else if (o < 0) && (v > (math.MaxInt64 + o)) { return Int64Value(math.MinInt64) + } else if (o < 0) && (v > (math.MaxInt64 + o)) { + return Int64Value(math.MaxInt64) } return v - o } @@ -6115,9 +6115,9 @@ func (v Fix64Value) SaturatingMinus(other NumberValue) NumberValue { o := other.(Fix64Value) // INT32-C if (o > 0) && (v < (math.MinInt64 + o)) { - return Fix64Value(math.MaxInt64) - } else if (o < 0) && (v > (math.MaxInt64 + o)) { return Fix64Value(math.MinInt64) + } else if (o < 0) && (v > (math.MaxInt64 + o)) { + return Fix64Value(math.MaxInt64) } return v - o } From d5698410d4a199ed7effe0fee050fe2832157210 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Thu, 15 Apr 2021 17:59:48 -0700 Subject: [PATCH 08/11] test saturating arithmetic functions --- runtime/tests/interpreter/arithmetic_test.go | 625 +++++++++++++++++++ 1 file changed, 625 insertions(+) diff --git a/runtime/tests/interpreter/arithmetic_test.go b/runtime/tests/interpreter/arithmetic_test.go index 972d331f4b..beffb72702 100644 --- a/runtime/tests/interpreter/arithmetic_test.go +++ b/runtime/tests/interpreter/arithmetic_test.go @@ -20,9 +20,12 @@ package interpreter_test import ( "fmt" + "math" + "strings" "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/onflow/cadence/runtime/interpreter" "github.com/onflow/cadence/runtime/sema" @@ -200,3 +203,625 @@ func TestInterpretModOperator(t *testing.T) { }) } } + +func TestInterpretSaturatedArithmeticFunctions(t *testing.T) { + + t.Parallel() + + type testCall struct { + left, right interpreter.Value + expected interpreter.EquatableValue + } + + type testCalls struct { + underflow, overflow testCall + } + + type testCase struct { + add, subtract, multiply, divide testCalls + } + + testCases := map[sema.Type]testCase{ + sema.Int8Type: { + add: testCalls{ + overflow: testCall{ + interpreter.Int8Value(math.MaxInt8), + interpreter.Int8Value(2), + interpreter.Int8Value(math.MaxInt8), + }, + underflow: testCall{ + interpreter.Int8Value(math.MinInt8), + interpreter.Int8Value(-2), + interpreter.Int8Value(math.MinInt8), + }, + }, + subtract: testCalls{ + overflow: testCall{ + interpreter.Int8Value(math.MaxInt8), + interpreter.Int8Value(-2), + interpreter.Int8Value(math.MaxInt8), + }, + underflow: testCall{ + interpreter.Int8Value(math.MinInt8), + interpreter.Int8Value(2), + interpreter.Int8Value(math.MinInt8), + }, + }, + multiply: testCalls{ + overflow: testCall{ + interpreter.Int8Value(math.MaxInt8), + interpreter.Int8Value(2), + interpreter.Int8Value(math.MaxInt8), + }, + underflow: testCall{ + interpreter.Int8Value(math.MinInt8), + interpreter.Int8Value(2), + interpreter.Int8Value(math.MinInt8), + }, + }, + divide: testCalls{ + overflow: testCall{ + interpreter.Int8Value(math.MinInt8), + interpreter.Int8Value(-1), + interpreter.Int8Value(math.MaxInt8), + }, + }, + }, + sema.Int16Type: { + add: testCalls{ + overflow: testCall{ + interpreter.Int16Value(math.MaxInt16), + interpreter.Int16Value(2), + interpreter.Int16Value(math.MaxInt16), + }, + underflow: testCall{ + interpreter.Int16Value(math.MinInt16), + interpreter.Int16Value(-2), + interpreter.Int16Value(math.MinInt16), + }, + }, + subtract: testCalls{ + overflow: testCall{ + interpreter.Int16Value(math.MaxInt16), + interpreter.Int16Value(-2), + interpreter.Int16Value(math.MaxInt16), + }, + underflow: testCall{ + interpreter.Int16Value(math.MinInt16), + interpreter.Int16Value(2), + interpreter.Int16Value(math.MinInt16), + }, + }, + multiply: testCalls{ + overflow: testCall{ + interpreter.Int16Value(math.MaxInt16), + interpreter.Int16Value(2), + interpreter.Int16Value(math.MaxInt16), + }, + underflow: testCall{ + interpreter.Int16Value(math.MinInt16), + interpreter.Int16Value(2), + interpreter.Int16Value(math.MinInt16), + }, + }, + divide: testCalls{ + overflow: testCall{ + interpreter.Int16Value(math.MinInt16), + interpreter.Int16Value(-1), + interpreter.Int16Value(math.MaxInt16), + }, + }, + }, + sema.Int32Type: { + add: testCalls{ + overflow: testCall{ + interpreter.Int32Value(math.MaxInt32), + interpreter.Int32Value(2), + interpreter.Int32Value(math.MaxInt32), + }, + underflow: testCall{ + interpreter.Int32Value(math.MinInt32), + interpreter.Int32Value(-2), + interpreter.Int32Value(math.MinInt32), + }, + }, + subtract: testCalls{ + overflow: testCall{ + interpreter.Int32Value(math.MaxInt32), + interpreter.Int32Value(-2), + interpreter.Int32Value(math.MaxInt32), + }, + underflow: testCall{ + interpreter.Int32Value(math.MinInt32), + interpreter.Int32Value(2), + interpreter.Int32Value(math.MinInt32), + }, + }, + multiply: testCalls{ + overflow: testCall{ + interpreter.Int32Value(math.MaxInt32), + interpreter.Int32Value(2), + interpreter.Int32Value(math.MaxInt32), + }, + underflow: testCall{ + interpreter.Int32Value(math.MinInt32), + interpreter.Int32Value(2), + interpreter.Int32Value(math.MinInt32), + }, + }, + divide: testCalls{ + overflow: testCall{ + interpreter.Int32Value(math.MinInt32), + interpreter.Int32Value(-1), + interpreter.Int32Value(math.MaxInt32), + }, + }, + }, + sema.Int64Type: { + add: testCalls{ + overflow: testCall{ + interpreter.Int64Value(math.MaxInt64), + interpreter.Int64Value(2), + interpreter.Int64Value(math.MaxInt64), + }, + underflow: testCall{ + interpreter.Int64Value(math.MinInt64), + interpreter.Int64Value(-2), + interpreter.Int64Value(math.MinInt64), + }, + }, + subtract: testCalls{ + overflow: testCall{ + interpreter.Int64Value(math.MaxInt64), + interpreter.Int64Value(-2), + interpreter.Int64Value(math.MaxInt64), + }, + underflow: testCall{ + interpreter.Int64Value(math.MinInt64), + interpreter.Int64Value(2), + interpreter.Int64Value(math.MinInt64), + }, + }, + multiply: testCalls{ + overflow: testCall{ + interpreter.Int64Value(math.MaxInt64), + interpreter.Int64Value(2), + interpreter.Int64Value(math.MaxInt64), + }, + underflow: testCall{ + interpreter.Int64Value(math.MinInt64), + interpreter.Int64Value(2), + interpreter.Int64Value(math.MinInt64), + }, + }, + divide: testCalls{ + overflow: testCall{ + interpreter.Int64Value(math.MinInt64), + interpreter.Int64Value(-1), + interpreter.Int64Value(math.MaxInt64), + }, + }, + }, + sema.Int128Type: { + add: testCalls{ + overflow: testCall{ + interpreter.NewInt128ValueFromBigInt(sema.Int128TypeMaxIntBig), + interpreter.NewInt128ValueFromInt64(2), + interpreter.NewInt128ValueFromBigInt(sema.Int128TypeMaxIntBig), + }, + underflow: testCall{ + interpreter.NewInt128ValueFromBigInt(sema.Int128TypeMinIntBig), + interpreter.NewInt128ValueFromInt64(-2), + interpreter.NewInt128ValueFromBigInt(sema.Int128TypeMinIntBig), + }, + }, + subtract: testCalls{ + overflow: testCall{ + interpreter.NewInt128ValueFromBigInt(sema.Int128TypeMaxIntBig), + interpreter.NewInt128ValueFromInt64(-2), + interpreter.NewInt128ValueFromBigInt(sema.Int128TypeMaxIntBig), + }, + underflow: testCall{ + interpreter.NewInt128ValueFromBigInt(sema.Int128TypeMinIntBig), + interpreter.NewInt128ValueFromInt64(2), + interpreter.NewInt128ValueFromBigInt(sema.Int128TypeMinIntBig), + }, + }, + multiply: testCalls{ + overflow: testCall{ + interpreter.NewInt128ValueFromBigInt(sema.Int128TypeMaxIntBig), + interpreter.NewInt128ValueFromInt64(2), + interpreter.NewInt128ValueFromBigInt(sema.Int128TypeMaxIntBig), + }, + underflow: testCall{ + interpreter.NewInt128ValueFromBigInt(sema.Int128TypeMinIntBig), + interpreter.NewInt128ValueFromInt64(2), + interpreter.NewInt128ValueFromBigInt(sema.Int128TypeMinIntBig), + }, + }, + divide: testCalls{ + overflow: testCall{ + interpreter.NewInt128ValueFromBigInt(sema.Int128TypeMinIntBig), + interpreter.NewInt128ValueFromInt64(-1), + interpreter.NewInt128ValueFromBigInt(sema.Int128TypeMaxIntBig), + }, + }, + }, + sema.Int256Type: { + add: testCalls{ + overflow: testCall{ + interpreter.NewInt256ValueFromBigInt(sema.Int256TypeMaxIntBig), + interpreter.NewInt256ValueFromInt64(2), + interpreter.NewInt256ValueFromBigInt(sema.Int256TypeMaxIntBig), + }, + underflow: testCall{ + interpreter.NewInt256ValueFromBigInt(sema.Int256TypeMinIntBig), + interpreter.NewInt256ValueFromInt64(-2), + interpreter.NewInt256ValueFromBigInt(sema.Int256TypeMinIntBig), + }, + }, + subtract: testCalls{ + overflow: testCall{ + interpreter.NewInt256ValueFromBigInt(sema.Int256TypeMaxIntBig), + interpreter.NewInt256ValueFromInt64(-2), + interpreter.NewInt256ValueFromBigInt(sema.Int256TypeMaxIntBig), + }, + underflow: testCall{ + interpreter.NewInt256ValueFromBigInt(sema.Int256TypeMinIntBig), + interpreter.NewInt256ValueFromInt64(2), + interpreter.NewInt256ValueFromBigInt(sema.Int256TypeMinIntBig), + }, + }, + multiply: testCalls{ + overflow: testCall{ + interpreter.NewInt256ValueFromBigInt(sema.Int256TypeMaxIntBig), + interpreter.NewInt256ValueFromInt64(2), + interpreter.NewInt256ValueFromBigInt(sema.Int256TypeMaxIntBig), + }, + underflow: testCall{ + interpreter.NewInt256ValueFromBigInt(sema.Int256TypeMinIntBig), + interpreter.NewInt256ValueFromInt64(2), + interpreter.NewInt256ValueFromBigInt(sema.Int256TypeMinIntBig), + }, + }, + divide: testCalls{ + overflow: testCall{ + interpreter.NewInt256ValueFromBigInt(sema.Int256TypeMinIntBig), + interpreter.NewInt256ValueFromInt64(-1), + interpreter.NewInt256ValueFromBigInt(sema.Int256TypeMaxIntBig), + }, + }, + }, + sema.Fix64Type: { + add: testCalls{ + overflow: testCall{ + interpreter.Fix64Value(math.MaxInt64), + interpreter.NewFix64ValueWithInteger(2), + interpreter.Fix64Value(math.MaxInt64), + }, + underflow: testCall{ + interpreter.Fix64Value(math.MinInt64), + interpreter.NewFix64ValueWithInteger(-2), + interpreter.Fix64Value(math.MinInt64), + }, + }, + subtract: testCalls{ + overflow: testCall{ + interpreter.Fix64Value(math.MaxInt64), + interpreter.NewFix64ValueWithInteger(-2), + interpreter.Fix64Value(math.MaxInt64), + }, + underflow: testCall{ + interpreter.Fix64Value(math.MinInt64), + interpreter.NewFix64ValueWithInteger(2), + interpreter.Fix64Value(math.MinInt64), + }, + }, + multiply: testCalls{ + overflow: testCall{ + interpreter.Fix64Value(math.MaxInt64), + interpreter.NewFix64ValueWithInteger(2), + interpreter.Fix64Value(math.MaxInt64), + }, + underflow: testCall{ + interpreter.Fix64Value(math.MinInt64), + interpreter.NewFix64ValueWithInteger(2), + interpreter.Fix64Value(math.MinInt64), + }, + }, + divide: testCalls{ + overflow: testCall{ + interpreter.Fix64Value(math.MinInt64), + interpreter.NewFix64ValueWithInteger(-1), + interpreter.Fix64Value(math.MaxInt64), + }, + }, + }, + sema.UIntType: { + subtract: testCalls{ + underflow: testCall{ + interpreter.NewUIntValueFromBigInt(sema.UIntTypeMin), + interpreter.NewUIntValueFromUint64(2), + interpreter.NewUIntValueFromBigInt(sema.UIntTypeMin), + }, + }, + }, + sema.UInt8Type: { + add: testCalls{ + overflow: testCall{ + interpreter.UInt8Value(math.MaxUint8), + interpreter.UInt8Value(2), + interpreter.UInt8Value(math.MaxUint8), + }, + }, + subtract: testCalls{ + underflow: testCall{ + interpreter.UInt8Value(0), + interpreter.UInt8Value(2), + interpreter.UInt8Value(0), + }, + }, + multiply: testCalls{ + overflow: testCall{ + interpreter.UInt8Value(math.MaxUint8), + interpreter.UInt8Value(2), + interpreter.UInt8Value(math.MaxUint8), + }, + }, + }, + sema.UInt16Type: { + add: testCalls{ + overflow: testCall{ + interpreter.UInt16Value(math.MaxUint16), + interpreter.UInt16Value(2), + interpreter.UInt16Value(math.MaxUint16), + }, + }, + subtract: testCalls{ + underflow: testCall{ + interpreter.UInt16Value(0), + interpreter.UInt16Value(2), + interpreter.UInt16Value(0), + }, + }, + multiply: testCalls{ + overflow: testCall{ + interpreter.UInt16Value(math.MaxUint16), + interpreter.UInt16Value(2), + interpreter.UInt16Value(math.MaxUint16), + }, + }, + }, + sema.UInt32Type: { + add: testCalls{ + overflow: testCall{ + interpreter.UInt32Value(math.MaxUint32), + interpreter.UInt32Value(2), + interpreter.UInt32Value(math.MaxUint32), + }, + }, + subtract: testCalls{ + underflow: testCall{ + interpreter.UInt32Value(0), + interpreter.UInt32Value(2), + interpreter.UInt32Value(0), + }, + }, + multiply: testCalls{ + overflow: testCall{ + interpreter.UInt32Value(math.MaxUint32), + interpreter.UInt32Value(2), + interpreter.UInt32Value(math.MaxUint32), + }, + }, + }, + sema.UInt64Type: { + add: testCalls{ + overflow: testCall{ + interpreter.UInt64Value(math.MaxUint64), + interpreter.UInt64Value(2), + interpreter.UInt64Value(math.MaxUint64), + }, + }, + subtract: testCalls{ + underflow: testCall{ + interpreter.UInt64Value(0), + interpreter.UInt64Value(2), + interpreter.UInt64Value(0), + }, + }, + multiply: testCalls{ + overflow: testCall{ + interpreter.UInt64Value(math.MaxUint64), + interpreter.UInt64Value(2), + interpreter.UInt64Value(math.MaxUint64), + }, + }, + }, + sema.UInt128Type: { + add: testCalls{ + overflow: testCall{ + interpreter.NewUInt128ValueFromBigInt(sema.UInt128TypeMaxIntBig), + interpreter.NewUInt128ValueFromUint64(2), + interpreter.NewUInt128ValueFromBigInt(sema.UInt128TypeMaxIntBig), + }, + }, + subtract: testCalls{ + underflow: testCall{ + interpreter.NewUInt128ValueFromBigInt(sema.UInt128TypeMinIntBig), + interpreter.NewUInt128ValueFromUint64(2), + interpreter.NewUInt128ValueFromBigInt(sema.UInt128TypeMinIntBig), + }, + }, + multiply: testCalls{ + overflow: testCall{ + interpreter.NewUInt128ValueFromBigInt(sema.UInt128TypeMaxIntBig), + interpreter.NewUInt128ValueFromUint64(2), + interpreter.NewUInt128ValueFromBigInt(sema.UInt128TypeMaxIntBig), + }, + }, + }, + sema.UInt256Type: { + add: testCalls{ + overflow: testCall{ + interpreter.NewUInt256ValueFromBigInt(sema.UInt256TypeMaxIntBig), + interpreter.NewUInt256ValueFromUint64(2), + interpreter.NewUInt256ValueFromBigInt(sema.UInt256TypeMaxIntBig), + }, + }, + subtract: testCalls{ + underflow: testCall{ + interpreter.NewUInt256ValueFromBigInt(sema.UInt256TypeMinIntBig), + interpreter.NewUInt256ValueFromUint64(2), + interpreter.NewUInt256ValueFromBigInt(sema.UInt256TypeMinIntBig), + }, + }, + multiply: testCalls{ + overflow: testCall{ + interpreter.NewUInt256ValueFromBigInt(sema.UInt256TypeMaxIntBig), + interpreter.NewUInt256ValueFromUint64(2), + interpreter.NewUInt256ValueFromBigInt(sema.UInt256TypeMaxIntBig), + }, + }, + }, + sema.UFix64Type: { + add: testCalls{ + overflow: testCall{ + interpreter.UFix64Value(math.MaxUint64), + interpreter.NewUFix64ValueWithInteger(2), + interpreter.UFix64Value(math.MaxUint64), + }, + }, + subtract: testCalls{ + underflow: testCall{ + interpreter.UFix64Value(0), + interpreter.NewUFix64ValueWithInteger(2), + interpreter.UFix64Value(0), + }, + }, + multiply: testCalls{ + overflow: testCall{ + interpreter.UFix64Value(math.MaxUint64), + interpreter.NewUFix64ValueWithInteger(2), + interpreter.UFix64Value(math.MaxUint64), + }, + }, + }, + } + + // Verify all test cases exist + + for _, ty := range append( + sema.AllSignedIntegerTypes[:], + sema.AllSignedFixedPointTypes..., + ) { + + testCase, ok := testCases[ty] + + if ty == sema.IntType { + require.False(t, ok, "invalid test case for %s", ty) + } else { + require.True(t, ok, "missing test case for %s", ty) + + require.NotNil(t, testCase.add.overflow.expected) + require.NotNil(t, testCase.add.underflow.expected) + + require.NotNil(t, testCase.subtract.overflow.expected) + require.NotNil(t, testCase.subtract.underflow.expected) + + require.NotNil(t, testCase.multiply.overflow.expected) + require.NotNil(t, testCase.multiply.underflow.expected) + + require.NotNil(t, testCase.divide.overflow.expected) + require.Nil(t, testCase.divide.underflow.expected) + } + } + + for _, ty := range append( + sema.AllUnsignedIntegerTypes[:], + sema.AllUnsignedFixedPointTypes..., + ) { + + if strings.HasPrefix(ty.String(), "Word") { + continue + } + + testCase, ok := testCases[ty] + require.True(t, ok, "missing test case for %s", ty) + + if ty == sema.UIntType { + + require.Nil(t, testCase.add.overflow.expected) + require.Nil(t, testCase.add.underflow.expected) + + require.Nil(t, testCase.subtract.overflow.expected) + require.NotNil(t, testCase.subtract.underflow.expected) + + require.Nil(t, testCase.multiply.overflow.expected) + require.Nil(t, testCase.multiply.underflow.expected) + + require.Nil(t, testCase.divide.overflow.expected) + require.Nil(t, testCase.divide.underflow.expected) + } else { + require.NotNil(t, testCase.add.overflow.expected) + require.Nil(t, testCase.add.underflow.expected) + + require.Nil(t, testCase.subtract.overflow.expected) + require.NotNil(t, testCase.subtract.underflow.expected) + + require.NotNil(t, testCase.multiply.overflow.expected) + require.Nil(t, testCase.multiply.underflow.expected) + + require.Nil(t, testCase.divide.overflow.expected) + require.Nil(t, testCase.divide.underflow.expected) + } + } + + test := func(ty sema.Type, method string, calls testCalls) { + + method = fmt.Sprintf("saturating%s", method) + + for kind, call := range map[string]testCall{ + "overflow": calls.overflow, + "underflow": calls.underflow, + } { + + if call.expected == nil { + continue + } + + t.Run(fmt.Sprintf("%s %s %s", ty, method, kind), func(t *testing.T) { + + inter := parseCheckAndInterpret(t, + fmt.Sprintf( + ` + fun test(a: %[1]s, b: %[1]s): %[1]s { + return a.%[2]s(b) + } + `, + ty, + method, + ), + ) + + result, err := inter.Invoke("test", call.left, call.right) + require.NoError(t, err) + + require.True(t, + bool(call.expected.Equal(inter, result)), + fmt.Sprintf( + "%s(%s, %s) = %s != %s", + method, call.left, call.right, result, call.expected, + ), + ) + }) + } + } + + for ty, testCase := range testCases { + test(ty, "Add", testCase.add) + test(ty, "Subtract", testCase.subtract) + test(ty, "Multiply", testCase.multiply) + test(ty, "Divide", testCase.divide) + } +} From a047eece6ab8e4482f533fd793299c68a7b10b9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Thu, 15 Apr 2021 18:05:02 -0700 Subject: [PATCH 09/11] update arguments for Equal call --- runtime/tests/interpreter/arithmetic_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/tests/interpreter/arithmetic_test.go b/runtime/tests/interpreter/arithmetic_test.go index beffb72702..0709c14acb 100644 --- a/runtime/tests/interpreter/arithmetic_test.go +++ b/runtime/tests/interpreter/arithmetic_test.go @@ -808,7 +808,7 @@ func TestInterpretSaturatedArithmeticFunctions(t *testing.T) { require.NoError(t, err) require.True(t, - bool(call.expected.Equal(inter, result)), + bool(call.expected.Equal(result, inter, false)), fmt.Sprintf( "%s(%s, %s) = %s != %s", method, call.left, call.right, result, call.expected, From 630060048fe63e6498e1587570fcbaf512552098 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Mon, 19 Apr 2021 12:40:15 -0700 Subject: [PATCH 10/11] refactor addition of saturating arithmetic function members --- runtime/sema/type.go | 285 ++++++++++++++++++++++--------------------- 1 file changed, 143 insertions(+), 142 deletions(-) diff --git a/runtime/sema/type.go b/runtime/sema/type.go index 996a4c1daa..6a6ffa6dc1 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -718,19 +718,99 @@ type FractionalRangedType interface { MaxFractional() *big.Int } +// SaturatingArithmeticType is a type that supports saturating arithmetic functions +// +type SaturatingArithmeticType interface { + Type + SupportsSaturatingAdd() bool + SupportsSaturatingSubtract() bool + SupportsSaturatingMultiply() bool + SupportsSaturatingDivide() bool +} + +const NumericTypeSaturatingAddFunctionName = "saturatingAdd" +const numericTypeSaturatingAddFunctionDocString = ` +self + other, saturating at the numeric bounds instead of overflowing. +` + +const NumericTypeSaturatingSubtractFunctionName = "saturatingSubtract" +const numericTypeSaturatingSubtractFunctionDocString = ` +self - other, saturating at the numeric bounds instead of overflowing. +` +const NumericTypeSaturatingMultiplyFunctionName = "saturatingMultiply" +const numericTypeSaturatingMultiplyFunctionDocString = ` +self * other, saturating at the numeric bounds instead of overflowing. +` + +const NumericTypeSaturatingDivideFunctionName = "saturatingDivide" +const numericTypeSaturatingDivideFunctionDocString = ` +self / other, saturating at the numeric bounds instead of overflowing. +` + +func addSaturatingArithmeticFunctions(t SaturatingArithmeticType, members map[string]MemberResolver) { + + arithmeticFunctionType := &FunctionType{ + Parameters: []*Parameter{ + { + Label: ArgumentLabelNotRequired, + Identifier: "other", + TypeAnnotation: NewTypeAnnotation(t), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation(t), + } + + addArithmeticFunction := func(name string, docString string) { + members[name] = MemberResolver{ + Kind: common.DeclarationKindFunction, + Resolve: func(identifier string, targetRange ast.Range, report func(error)) *Member { + return NewPublicFunctionMember(t, name, arithmeticFunctionType, docString) + }, + } + } + + if t.SupportsSaturatingAdd() { + addArithmeticFunction( + NumericTypeSaturatingAddFunctionName, + numericTypeSaturatingAddFunctionDocString, + ) + } + + if t.SupportsSaturatingSubtract() { + addArithmeticFunction( + NumericTypeSaturatingSubtractFunctionName, + numericTypeSaturatingSubtractFunctionDocString, + ) + } + + if t.SupportsSaturatingMultiply() { + addArithmeticFunction( + NumericTypeSaturatingMultiplyFunctionName, + numericTypeSaturatingMultiplyFunctionDocString, + ) + } + + if t.SupportsSaturatingDivide() { + addArithmeticFunction( + NumericTypeSaturatingDivideFunctionName, + numericTypeSaturatingDivideFunctionDocString, + ) + } +} + // NumericType represent all the types in the integer range // and non-fractional ranged types. // type NumericType struct { - name string - minInt *big.Int - maxInt *big.Int - saturatingAdd bool - saturatingSubtract bool - saturatingMultiply bool - saturatingDivide bool - memberResolvers map[string]MemberResolver - memberResolversOnce sync.Once + name string + minInt *big.Int + maxInt *big.Int + supportsSaturatingAdd bool + supportsSaturatingSubtract bool + supportsSaturatingMultiply bool + supportsSaturatingDivide bool + memberResolvers map[string]MemberResolver + memberResolversOnce sync.Once } var _ IntegerRangedType = &NumericType{} @@ -746,25 +826,41 @@ func (t *NumericType) WithIntRange(min *big.Int, max *big.Int) *NumericType { } func (t *NumericType) WithSaturatingAdd() *NumericType { - t.saturatingAdd = true + t.supportsSaturatingAdd = true return t } func (t *NumericType) WithSaturatingSubtract() *NumericType { - t.saturatingSubtract = true + t.supportsSaturatingSubtract = true return t } func (t *NumericType) WithSaturatingMultiply() *NumericType { - t.saturatingMultiply = true + t.supportsSaturatingMultiply = true return t } func (t *NumericType) WithSaturatingDivide() *NumericType { - t.saturatingDivide = true + t.supportsSaturatingDivide = true return t } +func (t *NumericType) SupportsSaturatingAdd() bool { + return t.supportsSaturatingAdd +} + +func (t *NumericType) SupportsSaturatingSubtract() bool { + return t.supportsSaturatingSubtract +} + +func (t *NumericType) SupportsSaturatingMultiply() bool { + return t.supportsSaturatingMultiply +} + +func (t *NumericType) SupportsSaturatingDivide() bool { + return t.supportsSaturatingDivide +} + func (*NumericType) IsType() {} func (t *NumericType) String() string { @@ -839,76 +935,11 @@ func (t *NumericType) GetMembers() map[string]MemberResolver { return t.memberResolvers } -const NumericTypeSaturatingAddFunctionName = "saturatingAdd" -const numericTypeSaturatingAddFunctionDocString = ` -self + other, saturating at the numeric bounds instead of overflowing. -` - -const NumericTypeSaturatingSubtractFunctionName = "saturatingSubtract" -const numericTypeSaturatingSubtractFunctionDocString = ` -self - other, saturating at the numeric bounds instead of overflowing. -` -const NumericTypeSaturatingMultiplyFunctionName = "saturatingMultiply" -const numericTypeSaturatingMultiplyFunctionDocString = ` -self * other, saturating at the numeric bounds instead of overflowing. -` - -const NumericTypeSaturatingDivideFunctionName = "saturatingDivide" -const numericTypeSaturatingDivideFunctionDocString = ` -self / other, saturating at the numeric bounds instead of overflowing. -` - func (t *NumericType) initializeMemberResolvers() { t.memberResolversOnce.Do(func() { members := map[string]MemberResolver{} - arithmeticFunctionType := &FunctionType{ - Parameters: []*Parameter{ - { - Label: ArgumentLabelNotRequired, - Identifier: "other", - TypeAnnotation: NewTypeAnnotation(t), - }, - }, - ReturnTypeAnnotation: NewTypeAnnotation(t), - } - - addArithmeticFunction := func(name string, docString string) { - members[name] = MemberResolver{ - Kind: common.DeclarationKindFunction, - Resolve: func(identifier string, targetRange ast.Range, report func(error)) *Member { - return NewPublicFunctionMember(t, name, arithmeticFunctionType, docString) - }, - } - } - - if t.saturatingAdd { - addArithmeticFunction( - NumericTypeSaturatingAddFunctionName, - numericTypeSaturatingAddFunctionDocString, - ) - } - - if t.saturatingSubtract { - addArithmeticFunction( - NumericTypeSaturatingSubtractFunctionName, - numericTypeSaturatingSubtractFunctionDocString, - ) - } - - if t.saturatingMultiply { - addArithmeticFunction( - NumericTypeSaturatingMultiplyFunctionName, - numericTypeSaturatingMultiplyFunctionDocString, - ) - } - - if t.saturatingDivide { - addArithmeticFunction( - NumericTypeSaturatingDivideFunctionName, - numericTypeSaturatingDivideFunctionDocString, - ) - } + addSaturatingArithmeticFunctions(t, members) t.memberResolvers = withBuiltinMembers(t, members) }) @@ -917,18 +948,18 @@ func (t *NumericType) initializeMemberResolvers() { // FixedPointNumericType represents all the types in the fixed-point range. // type FixedPointNumericType struct { - name string - scale uint - minInt *big.Int - maxInt *big.Int - minFractional *big.Int - maxFractional *big.Int - saturatingAdd bool - saturatingSubtract bool - saturatingMultiply bool - saturatingDivide bool - memberResolvers map[string]MemberResolver - memberResolversOnce sync.Once + name string + scale uint + minInt *big.Int + maxInt *big.Int + minFractional *big.Int + maxFractional *big.Int + supportsSaturatingAdd bool + supportsSaturatingSubtract bool + supportsSaturatingMultiply bool + supportsSaturatingDivide bool + memberResolvers map[string]MemberResolver + memberResolversOnce sync.Once } var _ FractionalRangedType = &FixedPointNumericType{} @@ -961,25 +992,41 @@ func (t *FixedPointNumericType) WithScale(scale uint) *FixedPointNumericType { } func (t *FixedPointNumericType) WithSaturatingAdd() *FixedPointNumericType { - t.saturatingAdd = true + t.supportsSaturatingAdd = true return t } func (t *FixedPointNumericType) WithSaturatingSubtract() *FixedPointNumericType { - t.saturatingSubtract = true + t.supportsSaturatingSubtract = true return t } func (t *FixedPointNumericType) WithSaturatingMultiply() *FixedPointNumericType { - t.saturatingMultiply = true + t.supportsSaturatingMultiply = true return t } func (t *FixedPointNumericType) WithSaturatingDivide() *FixedPointNumericType { - t.saturatingDivide = true + t.supportsSaturatingDivide = true return t } +func (t *FixedPointNumericType) SupportsSaturatingAdd() bool { + return t.supportsSaturatingAdd +} + +func (t *FixedPointNumericType) SupportsSaturatingSubtract() bool { + return t.supportsSaturatingSubtract +} + +func (t *FixedPointNumericType) SupportsSaturatingMultiply() bool { + return t.supportsSaturatingMultiply +} + +func (t *FixedPointNumericType) SupportsSaturatingDivide() bool { + return t.supportsSaturatingDivide +} + func (*FixedPointNumericType) IsType() {} func (t *FixedPointNumericType) String() string { @@ -1070,53 +1117,7 @@ func (t *FixedPointNumericType) initializeMemberResolvers() { t.memberResolversOnce.Do(func() { members := map[string]MemberResolver{} - arithmeticFunctionType := &FunctionType{ - Parameters: []*Parameter{ - { - Label: ArgumentLabelNotRequired, - Identifier: "other", - TypeAnnotation: NewTypeAnnotation(t), - }, - }, - ReturnTypeAnnotation: NewTypeAnnotation(t), - } - - addArithmeticFunction := func(name string, docString string) { - members[name] = MemberResolver{ - Kind: common.DeclarationKindFunction, - Resolve: func(identifier string, targetRange ast.Range, report func(error)) *Member { - return NewPublicFunctionMember(t, name, arithmeticFunctionType, docString) - }, - } - } - - if t.saturatingAdd { - addArithmeticFunction( - NumericTypeSaturatingAddFunctionName, - numericTypeSaturatingAddFunctionDocString, - ) - } - - if t.saturatingSubtract { - addArithmeticFunction( - NumericTypeSaturatingSubtractFunctionName, - numericTypeSaturatingSubtractFunctionDocString, - ) - } - - if t.saturatingMultiply { - addArithmeticFunction( - NumericTypeSaturatingMultiplyFunctionName, - numericTypeSaturatingMultiplyFunctionDocString, - ) - } - - if t.saturatingDivide { - addArithmeticFunction( - NumericTypeSaturatingDivideFunctionName, - numericTypeSaturatingDivideFunctionDocString, - ) - } + addSaturatingArithmeticFunctions(t, members) t.memberResolvers = withBuiltinMembers(t, members) }) From 2fa0560dd3a15fee456515c44c6b77346bf04d60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Mon, 19 Apr 2021 13:21:38 -0700 Subject: [PATCH 11/11] refactor number value members --- runtime/interpreter/value.go | 1004 ++++++++-------------------------- 1 file changed, 221 insertions(+), 783 deletions(-) diff --git a/runtime/interpreter/value.go b/runtime/interpreter/value.go index ec1645e5b1..f89acc274f 100644 --- a/runtime/interpreter/value.go +++ b/runtime/interpreter/value.go @@ -856,16 +856,20 @@ func (v *ArrayValue) Equal(other Value, interpreter *Interpreter, loadDeferred b } // NumberValue - +// type NumberValue interface { EquatableValue ToInt() int Negate() NumberValue Plus(other NumberValue) NumberValue + SaturatingPlus(other NumberValue) NumberValue Minus(other NumberValue) NumberValue + SaturatingMinus(other NumberValue) NumberValue Mod(other NumberValue) NumberValue Mul(other NumberValue) NumberValue + SaturatingMul(other NumberValue) NumberValue Div(other NumberValue) NumberValue + SaturatingDiv(other NumberValue) NumberValue Less(other NumberValue) BoolValue LessEqual(other NumberValue) BoolValue Greater(other NumberValue) BoolValue @@ -873,6 +877,59 @@ type NumberValue interface { ToBigEndianBytes() []byte } +func getNumberValueMember(v NumberValue, name string) Value { + switch name { + + case sema.ToStringFunctionName: + return NewHostFunctionValue( + func(invocation Invocation) Value { + return NewStringValue(v.String()) + }, + ) + + case sema.ToBigEndianBytesFunctionName: + return NewHostFunctionValue( + func(invocation Invocation) Value { + return ByteSliceToByteArrayValue(v.ToBigEndianBytes()) + }, + ) + + case sema.NumericTypeSaturatingAddFunctionName: + return NewHostFunctionValue( + func(invocation Invocation) Value { + other := invocation.Arguments[0].(NumberValue) + return v.SaturatingPlus(other) + }, + ) + + case sema.NumericTypeSaturatingSubtractFunctionName: + return NewHostFunctionValue( + func(invocation Invocation) Value { + other := invocation.Arguments[0].(NumberValue) + return v.SaturatingMinus(other) + }, + ) + + case sema.NumericTypeSaturatingMultiplyFunctionName: + return NewHostFunctionValue( + func(invocation Invocation) Value { + other := invocation.Arguments[0].(NumberValue) + return v.SaturatingMul(other) + }, + ) + + case sema.NumericTypeSaturatingDivideFunctionName: + return NewHostFunctionValue( + func(invocation Invocation) Value { + other := invocation.Arguments[0].(NumberValue) + return v.SaturatingDiv(other) + }, + ) + } + + return nil +} + type IntegerValue interface { NumberValue BitwiseOr(other IntegerValue) IntegerValue @@ -884,7 +941,7 @@ type IntegerValue interface { // BigNumberValue. // Implemented by values with an integer value outside the range of int64 - +// type BigNumberValue interface { NumberValue ToBigInt() *big.Int @@ -981,6 +1038,10 @@ func (v IntValue) Plus(other NumberValue) NumberValue { return IntValue{res} } +func (v IntValue) SaturatingPlus(other NumberValue) NumberValue { + return v.Plus(other) +} + func (v IntValue) Minus(other NumberValue) NumberValue { o := other.(IntValue) res := new(big.Int) @@ -988,6 +1049,10 @@ func (v IntValue) Minus(other NumberValue) NumberValue { return IntValue{res} } +func (v IntValue) SaturatingMinus(other NumberValue) NumberValue { + return v.Minus(other) +} + func (v IntValue) Mod(other NumberValue) NumberValue { o := other.(IntValue) res := new(big.Int) @@ -1006,6 +1071,10 @@ func (v IntValue) Mul(other NumberValue) NumberValue { return IntValue{res} } +func (v IntValue) SaturatingMul(other NumberValue) NumberValue { + return v.Mul(other) +} + func (v IntValue) Div(other NumberValue) NumberValue { o := other.(IntValue) res := new(big.Int) @@ -1017,6 +1086,10 @@ func (v IntValue) Div(other NumberValue) NumberValue { return IntValue{res} } +func (v IntValue) SaturatingDiv(other NumberValue) NumberValue { + return v.Div(other) +} + func (v IntValue) Less(other NumberValue) BoolValue { cmp := v.BigInt.Cmp(other.(IntValue).BigInt) return cmp == -1 @@ -1094,24 +1167,7 @@ func (v IntValue) BitwiseRightShift(other IntegerValue) IntegerValue { } func (v IntValue) GetMember(_ *Interpreter, _ func() LocationRange, name string) Value { - switch name { - - case sema.ToStringFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - return NewStringValue(v.String()) - }, - ) - - case sema.ToBigEndianBytesFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - return ByteSliceToByteArrayValue(v.ToBigEndianBytes()) - }, - ) - } - - return nil + return getNumberValueMember(v, name) } func (IntValue) SetMember(_ *Interpreter, _ func() LocationRange, _ string, _ Value) { @@ -1404,56 +1460,7 @@ func (v Int8Value) BitwiseRightShift(other IntegerValue) IntegerValue { } func (v Int8Value) GetMember(_ *Interpreter, _ func() LocationRange, name string) Value { - switch name { - - case sema.ToStringFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - return NewStringValue(v.String()) - }, - ) - - case sema.ToBigEndianBytesFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - return ByteSliceToByteArrayValue(v.ToBigEndianBytes()) - }, - ) - - case sema.NumericTypeSaturatingAddFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - other := invocation.Arguments[0].(NumberValue) - return v.SaturatingPlus(other) - }, - ) - - case sema.NumericTypeSaturatingSubtractFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - other := invocation.Arguments[0].(NumberValue) - return v.SaturatingMinus(other) - }, - ) - - case sema.NumericTypeSaturatingMultiplyFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - other := invocation.Arguments[0].(NumberValue) - return v.SaturatingMul(other) - }, - ) - - case sema.NumericTypeSaturatingDivideFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - other := invocation.Arguments[0].(NumberValue) - return v.SaturatingDiv(other) - }, - ) - } - - return nil + return getNumberValueMember(v, name) } func (Int8Value) SetMember(_ *Interpreter, _ func() LocationRange, _ string, _ Value) { @@ -1746,56 +1753,7 @@ func (v Int16Value) BitwiseRightShift(other IntegerValue) IntegerValue { } func (v Int16Value) GetMember(_ *Interpreter, _ func() LocationRange, name string) Value { - switch name { - - case sema.ToStringFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - return NewStringValue(v.String()) - }, - ) - - case sema.ToBigEndianBytesFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - return ByteSliceToByteArrayValue(v.ToBigEndianBytes()) - }, - ) - - case sema.NumericTypeSaturatingAddFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - other := invocation.Arguments[0].(NumberValue) - return v.SaturatingPlus(other) - }, - ) - - case sema.NumericTypeSaturatingSubtractFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - other := invocation.Arguments[0].(NumberValue) - return v.SaturatingMinus(other) - }, - ) - - case sema.NumericTypeSaturatingMultiplyFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - other := invocation.Arguments[0].(NumberValue) - return v.SaturatingMul(other) - }, - ) - - case sema.NumericTypeSaturatingDivideFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - other := invocation.Arguments[0].(NumberValue) - return v.SaturatingDiv(other) - }, - ) - } - - return nil + return getNumberValueMember(v, name) } func (Int16Value) SetMember(_ *Interpreter, _ func() LocationRange, _ string, _ Value) { @@ -2090,56 +2048,7 @@ func (v Int32Value) BitwiseRightShift(other IntegerValue) IntegerValue { } func (v Int32Value) GetMember(_ *Interpreter, _ func() LocationRange, name string) Value { - switch name { - - case sema.ToStringFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - return NewStringValue(v.String()) - }, - ) - - case sema.ToBigEndianBytesFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - return ByteSliceToByteArrayValue(v.ToBigEndianBytes()) - }, - ) - - case sema.NumericTypeSaturatingAddFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - other := invocation.Arguments[0].(NumberValue) - return v.SaturatingPlus(other) - }, - ) - - case sema.NumericTypeSaturatingSubtractFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - other := invocation.Arguments[0].(NumberValue) - return v.SaturatingMinus(other) - }, - ) - - case sema.NumericTypeSaturatingMultiplyFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - other := invocation.Arguments[0].(NumberValue) - return v.SaturatingMul(other) - }, - ) - - case sema.NumericTypeSaturatingDivideFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - other := invocation.Arguments[0].(NumberValue) - return v.SaturatingDiv(other) - }, - ) - } - - return nil + return getNumberValueMember(v, name) } func (Int32Value) SetMember(_ *Interpreter, _ func() LocationRange, _ string, _ Value) { @@ -2433,67 +2342,18 @@ func (v Int64Value) BitwiseRightShift(other IntegerValue) IntegerValue { } func (v Int64Value) GetMember(_ *Interpreter, _ func() LocationRange, name string) Value { - switch name { + return getNumberValueMember(v, name) +} - case sema.ToStringFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - return NewStringValue(v.String()) - }, - ) +func (Int64Value) SetMember(_ *Interpreter, _ func() LocationRange, _ string, _ Value) { + panic(errors.NewUnreachableError()) +} - case sema.ToBigEndianBytesFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - return ByteSliceToByteArrayValue(v.ToBigEndianBytes()) - }, - ) - - case sema.NumericTypeSaturatingAddFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - other := invocation.Arguments[0].(NumberValue) - return v.SaturatingPlus(other) - }, - ) - - case sema.NumericTypeSaturatingSubtractFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - other := invocation.Arguments[0].(NumberValue) - return v.SaturatingMinus(other) - }, - ) - - case sema.NumericTypeSaturatingMultiplyFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - other := invocation.Arguments[0].(NumberValue) - return v.SaturatingMul(other) - }, - ) - - case sema.NumericTypeSaturatingDivideFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - other := invocation.Arguments[0].(NumberValue) - return v.SaturatingDiv(other) - }, - ) - } - - return nil -} - -func (Int64Value) SetMember(_ *Interpreter, _ func() LocationRange, _ string, _ Value) { - panic(errors.NewUnreachableError()) -} - -func (v Int64Value) ToBigEndianBytes() []byte { - b := make([]byte, 8) - binary.BigEndian.PutUint64(b, uint64(v)) - return b -} +func (v Int64Value) ToBigEndianBytes() []byte { + b := make([]byte, 8) + binary.BigEndian.PutUint64(b, uint64(v)) + return b +} func (v Int64Value) ConformsToDynamicType(_ *Interpreter, dynamicType DynamicType, _ TypeConformanceResults) bool { numberType, ok := dynamicType.(NumberDynamicType) @@ -2847,56 +2707,7 @@ func (v Int128Value) BitwiseRightShift(other IntegerValue) IntegerValue { } func (v Int128Value) GetMember(_ *Interpreter, _ func() LocationRange, name string) Value { - switch name { - - case sema.ToStringFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - return NewStringValue(v.String()) - }, - ) - - case sema.ToBigEndianBytesFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - return ByteSliceToByteArrayValue(v.ToBigEndianBytes()) - }, - ) - - case sema.NumericTypeSaturatingAddFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - other := invocation.Arguments[0].(NumberValue) - return v.SaturatingPlus(other) - }, - ) - - case sema.NumericTypeSaturatingSubtractFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - other := invocation.Arguments[0].(NumberValue) - return v.SaturatingMinus(other) - }, - ) - - case sema.NumericTypeSaturatingMultiplyFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - other := invocation.Arguments[0].(NumberValue) - return v.SaturatingMul(other) - }, - ) - - case sema.NumericTypeSaturatingDivideFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - other := invocation.Arguments[0].(NumberValue) - return v.SaturatingDiv(other) - }, - ) - } - - return nil + return getNumberValueMember(v, name) } func (Int128Value) SetMember(_ *Interpreter, _ func() LocationRange, _ string, _ Value) { @@ -3260,56 +3071,7 @@ func (v Int256Value) BitwiseRightShift(other IntegerValue) IntegerValue { } func (v Int256Value) GetMember(_ *Interpreter, _ func() LocationRange, name string) Value { - switch name { - - case sema.ToStringFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - return NewStringValue(v.String()) - }, - ) - - case sema.ToBigEndianBytesFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - return ByteSliceToByteArrayValue(v.ToBigEndianBytes()) - }, - ) - - case sema.NumericTypeSaturatingAddFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - other := invocation.Arguments[0].(NumberValue) - return v.SaturatingPlus(other) - }, - ) - - case sema.NumericTypeSaturatingSubtractFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - other := invocation.Arguments[0].(NumberValue) - return v.SaturatingMinus(other) - }, - ) - - case sema.NumericTypeSaturatingMultiplyFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - other := invocation.Arguments[0].(NumberValue) - return v.SaturatingMul(other) - }, - ) - - case sema.NumericTypeSaturatingDivideFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - other := invocation.Arguments[0].(NumberValue) - return v.SaturatingDiv(other) - }, - ) - } - - return nil + return getNumberValueMember(v, name) } func (Int256Value) SetMember(_ *Interpreter, _ func() LocationRange, _ string, _ Value) { @@ -3423,6 +3185,10 @@ func (v UIntValue) Plus(other NumberValue) NumberValue { return UIntValue{res} } +func (v UIntValue) SaturatingPlus(other NumberValue) NumberValue { + return v.Plus(other) +} + func (v UIntValue) Minus(other NumberValue) NumberValue { o := other.(UIntValue) res := new(big.Int) @@ -3463,6 +3229,10 @@ func (v UIntValue) Mul(other NumberValue) NumberValue { return UIntValue{res} } +func (v UIntValue) SaturatingMul(other NumberValue) NumberValue { + return v.Mul(other) +} + func (v UIntValue) Div(other NumberValue) NumberValue { o := other.(UIntValue) res := new(big.Int) @@ -3474,6 +3244,10 @@ func (v UIntValue) Div(other NumberValue) NumberValue { return UIntValue{res} } +func (v UIntValue) SaturatingDiv(other NumberValue) NumberValue { + return v.Div(other) +} + func (v UIntValue) Less(other NumberValue) BoolValue { cmp := v.BigInt.Cmp(other.(UIntValue).BigInt) return cmp == -1 @@ -3551,32 +3325,7 @@ func (v UIntValue) BitwiseRightShift(other IntegerValue) IntegerValue { } func (v UIntValue) GetMember(_ *Interpreter, _ func() LocationRange, name string) Value { - switch name { - - case sema.ToStringFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - return NewStringValue(v.String()) - }, - ) - - case sema.ToBigEndianBytesFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - return ByteSliceToByteArrayValue(v.ToBigEndianBytes()) - }, - ) - - case sema.NumericTypeSaturatingSubtractFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - other := invocation.Arguments[0].(NumberValue) - return v.SaturatingMinus(other) - }, - ) - } - - return nil + return getNumberValueMember(v, name) } func (UIntValue) SetMember(_ *Interpreter, _ func() LocationRange, _ string, _ Value) { @@ -3717,6 +3466,10 @@ func (v UInt8Value) Div(other NumberValue) NumberValue { return v / o } +func (v UInt8Value) SaturatingDiv(other NumberValue) NumberValue { + return v.Div(other) +} + func (v UInt8Value) Less(other NumberValue) BoolValue { return v < other.(UInt8Value) } @@ -3796,48 +3549,7 @@ func (v UInt8Value) BitwiseRightShift(other IntegerValue) IntegerValue { } func (v UInt8Value) GetMember(_ *Interpreter, _ func() LocationRange, name string) Value { - switch name { - - case sema.ToStringFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - return NewStringValue(v.String()) - }, - ) - - case sema.ToBigEndianBytesFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - return ByteSliceToByteArrayValue(v.ToBigEndianBytes()) - }, - ) - - case sema.NumericTypeSaturatingAddFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - other := invocation.Arguments[0].(NumberValue) - return v.SaturatingPlus(other) - }, - ) - - case sema.NumericTypeSaturatingSubtractFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - other := invocation.Arguments[0].(NumberValue) - return v.SaturatingMinus(other) - }, - ) - - case sema.NumericTypeSaturatingMultiplyFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - other := invocation.Arguments[0].(NumberValue) - return v.SaturatingMul(other) - }, - ) - } - - return nil + return getNumberValueMember(v, name) } func (UInt8Value) SetMember(_ *Interpreter, _ func() LocationRange, _ string, _ Value) { @@ -3976,6 +3688,10 @@ func (v UInt16Value) Div(other NumberValue) NumberValue { return v / o } +func (v UInt16Value) SaturatingDiv(other NumberValue) NumberValue { + return v.Div(other) +} + func (v UInt16Value) Less(other NumberValue) BoolValue { return v < other.(UInt16Value) } @@ -4055,48 +3771,7 @@ func (v UInt16Value) BitwiseRightShift(other IntegerValue) IntegerValue { } func (v UInt16Value) GetMember(_ *Interpreter, _ func() LocationRange, name string) Value { - switch name { - - case sema.ToStringFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - return NewStringValue(v.String()) - }, - ) - - case sema.ToBigEndianBytesFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - return ByteSliceToByteArrayValue(v.ToBigEndianBytes()) - }, - ) - - case sema.NumericTypeSaturatingAddFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - other := invocation.Arguments[0].(NumberValue) - return v.SaturatingPlus(other) - }, - ) - - case sema.NumericTypeSaturatingSubtractFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - other := invocation.Arguments[0].(NumberValue) - return v.SaturatingMinus(other) - }, - ) - - case sema.NumericTypeSaturatingMultiplyFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - other := invocation.Arguments[0].(NumberValue) - return v.SaturatingMul(other) - }, - ) - } - - return nil + return getNumberValueMember(v, name) } func (UInt16Value) SetMember(_ *Interpreter, _ func() LocationRange, _ string, _ Value) { @@ -4238,6 +3913,10 @@ func (v UInt32Value) Div(other NumberValue) NumberValue { return v / o } +func (v UInt32Value) SaturatingDiv(other NumberValue) NumberValue { + return v.Div(other) +} + func (v UInt32Value) Less(other NumberValue) BoolValue { return v < other.(UInt32Value) } @@ -4317,48 +3996,7 @@ func (v UInt32Value) BitwiseRightShift(other IntegerValue) IntegerValue { } func (v UInt32Value) GetMember(_ *Interpreter, _ func() LocationRange, name string) Value { - switch name { - - case sema.ToStringFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - return NewStringValue(v.String()) - }, - ) - - case sema.ToBigEndianBytesFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - return ByteSliceToByteArrayValue(v.ToBigEndianBytes()) - }, - ) - - case sema.NumericTypeSaturatingAddFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - other := invocation.Arguments[0].(NumberValue) - return v.SaturatingPlus(other) - }, - ) - - case sema.NumericTypeSaturatingSubtractFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - other := invocation.Arguments[0].(NumberValue) - return v.SaturatingMinus(other) - }, - ) - - case sema.NumericTypeSaturatingMultiplyFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - other := invocation.Arguments[0].(NumberValue) - return v.SaturatingMul(other) - }, - ) - } - - return nil + return getNumberValueMember(v, name) } func (UInt32Value) SetMember(_ *Interpreter, _ func() LocationRange, _ string, _ Value) { @@ -4505,6 +4143,10 @@ func (v UInt64Value) Div(other NumberValue) NumberValue { return v / o } +func (v UInt64Value) SaturatingDiv(other NumberValue) NumberValue { + return v.Div(other) +} + func (v UInt64Value) Less(other NumberValue) BoolValue { return v < other.(UInt64Value) } @@ -4582,48 +4224,7 @@ func (v UInt64Value) BitwiseRightShift(other IntegerValue) IntegerValue { } func (v UInt64Value) GetMember(_ *Interpreter, _ func() LocationRange, name string) Value { - switch name { - - case sema.ToStringFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - return NewStringValue(v.String()) - }, - ) - - case sema.ToBigEndianBytesFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - return ByteSliceToByteArrayValue(v.ToBigEndianBytes()) - }, - ) - - case sema.NumericTypeSaturatingAddFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - other := invocation.Arguments[0].(NumberValue) - return v.SaturatingPlus(other) - }, - ) - - case sema.NumericTypeSaturatingSubtractFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - other := invocation.Arguments[0].(NumberValue) - return v.SaturatingMinus(other) - }, - ) - - case sema.NumericTypeSaturatingMultiplyFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - other := invocation.Arguments[0].(NumberValue) - return v.SaturatingMul(other) - }, - ) - } - - return nil + return getNumberValueMember(v, name) } func (UInt64Value) SetMember(_ *Interpreter, _ func() LocationRange, _ string, _ Value) { @@ -4827,6 +4428,10 @@ func (v UInt128Value) Div(other NumberValue) NumberValue { return UInt128Value{res} } +func (v UInt128Value) SaturatingDiv(other NumberValue) NumberValue { + return v.Div(other) +} + func (v UInt128Value) Less(other NumberValue) BoolValue { cmp := v.BigInt.Cmp(other.(UInt128Value).BigInt) return cmp == -1 @@ -4912,62 +4517,22 @@ func (v UInt128Value) BitwiseLeftShift(other IntegerValue) IntegerValue { res.Lsh(v.BigInt, uint(o.BigInt.Uint64())) return UInt128Value{res} } - -func (v UInt128Value) BitwiseRightShift(other IntegerValue) IntegerValue { - o := other.(UInt128Value) - res := new(big.Int) - if o.BigInt.Sign() < 0 { - panic(UnderflowError{}) - } - if !o.BigInt.IsUint64() { - panic(OverflowError{}) - } - res.Rsh(v.BigInt, uint(o.BigInt.Uint64())) - return UInt128Value{res} -} - -func (v UInt128Value) GetMember(_ *Interpreter, _ func() LocationRange, name string) Value { - switch name { - - case sema.ToStringFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - return NewStringValue(v.String()) - }, - ) - case sema.ToBigEndianBytesFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - return ByteSliceToByteArrayValue(v.ToBigEndianBytes()) - }, - ) - - case sema.NumericTypeSaturatingAddFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - other := invocation.Arguments[0].(NumberValue) - return v.SaturatingPlus(other) - }, - ) - - case sema.NumericTypeSaturatingSubtractFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - other := invocation.Arguments[0].(NumberValue) - return v.SaturatingMinus(other) - }, - ) - - case sema.NumericTypeSaturatingMultiplyFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - other := invocation.Arguments[0].(NumberValue) - return v.SaturatingMul(other) - }, - ) + +func (v UInt128Value) BitwiseRightShift(other IntegerValue) IntegerValue { + o := other.(UInt128Value) + res := new(big.Int) + if o.BigInt.Sign() < 0 { + panic(UnderflowError{}) + } + if !o.BigInt.IsUint64() { + panic(OverflowError{}) } + res.Rsh(v.BigInt, uint(o.BigInt.Uint64())) + return UInt128Value{res} +} - return nil +func (v UInt128Value) GetMember(_ *Interpreter, _ func() LocationRange, name string) Value { + return getNumberValueMember(v, name) } func (UInt128Value) SetMember(_ *Interpreter, _ func() LocationRange, _ string, _ Value) { @@ -5169,6 +4734,10 @@ func (v UInt256Value) Div(other NumberValue) NumberValue { return UInt256Value{res} } +func (v UInt256Value) SaturatingDiv(other NumberValue) NumberValue { + return v.Div(other) +} + func (v UInt256Value) Less(other NumberValue) BoolValue { cmp := v.BigInt.Cmp(other.(UInt256Value).BigInt) return cmp == -1 @@ -5269,48 +4838,7 @@ func (v UInt256Value) BitwiseRightShift(other IntegerValue) IntegerValue { } func (v UInt256Value) GetMember(_ *Interpreter, _ func() LocationRange, name string) Value { - switch name { - - case sema.ToStringFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - return NewStringValue(v.String()) - }, - ) - - case sema.ToBigEndianBytesFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - return ByteSliceToByteArrayValue(v.ToBigEndianBytes()) - }, - ) - - case sema.NumericTypeSaturatingAddFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - other := invocation.Arguments[0].(NumberValue) - return v.SaturatingPlus(other) - }, - ) - - case sema.NumericTypeSaturatingSubtractFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - other := invocation.Arguments[0].(NumberValue) - return v.SaturatingMinus(other) - }, - ) - - case sema.NumericTypeSaturatingMultiplyFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - other := invocation.Arguments[0].(NumberValue) - return v.SaturatingMul(other) - }, - ) - } - - return nil + return getNumberValueMember(v, name) } func (UInt256Value) SetMember(_ *Interpreter, _ func() LocationRange, _ string, _ Value) { @@ -5385,10 +4913,18 @@ func (v Word8Value) Plus(other NumberValue) NumberValue { return v + other.(Word8Value) } +func (v Word8Value) SaturatingPlus(_ NumberValue) NumberValue { + panic(errors.UnreachableError{}) +} + func (v Word8Value) Minus(other NumberValue) NumberValue { return v - other.(Word8Value) } +func (v Word8Value) SaturatingMinus(_ NumberValue) NumberValue { + panic(errors.UnreachableError{}) +} + func (v Word8Value) Mod(other NumberValue) NumberValue { o := other.(Word8Value) if o == 0 { @@ -5401,6 +4937,10 @@ func (v Word8Value) Mul(other NumberValue) NumberValue { return v * other.(Word8Value) } +func (v Word8Value) SaturatingMul(_ NumberValue) NumberValue { + panic(errors.UnreachableError{}) +} + func (v Word8Value) Div(other NumberValue) NumberValue { o := other.(Word8Value) if o == 0 { @@ -5409,6 +4949,10 @@ func (v Word8Value) Div(other NumberValue) NumberValue { return v / o } +func (v Word8Value) SaturatingDiv(_ NumberValue) NumberValue { + panic(errors.UnreachableError{}) +} + func (v Word8Value) Less(other NumberValue) BoolValue { return v < other.(Word8Value) } @@ -5463,24 +5007,7 @@ func (v Word8Value) BitwiseRightShift(other IntegerValue) IntegerValue { } func (v Word8Value) GetMember(_ *Interpreter, _ func() LocationRange, name string) Value { - switch name { - - case sema.ToStringFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - return NewStringValue(v.String()) - }, - ) - - case sema.ToBigEndianBytesFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - return ByteSliceToByteArrayValue(v.ToBigEndianBytes()) - }, - ) - } - - return nil + return getNumberValueMember(v, name) } func (Word8Value) SetMember(_ *Interpreter, _ func() LocationRange, _ string, _ Value) { @@ -5553,10 +5080,18 @@ func (v Word16Value) Plus(other NumberValue) NumberValue { return v + other.(Word16Value) } +func (v Word16Value) SaturatingPlus(_ NumberValue) NumberValue { + panic(errors.UnreachableError{}) +} + func (v Word16Value) Minus(other NumberValue) NumberValue { return v - other.(Word16Value) } +func (v Word16Value) SaturatingMinus(_ NumberValue) NumberValue { + panic(errors.UnreachableError{}) +} + func (v Word16Value) Mod(other NumberValue) NumberValue { o := other.(Word16Value) if o == 0 { @@ -5569,6 +5104,10 @@ func (v Word16Value) Mul(other NumberValue) NumberValue { return v * other.(Word16Value) } +func (v Word16Value) SaturatingMul(_ NumberValue) NumberValue { + panic(errors.UnreachableError{}) +} + func (v Word16Value) Div(other NumberValue) NumberValue { o := other.(Word16Value) if o == 0 { @@ -5577,6 +5116,10 @@ func (v Word16Value) Div(other NumberValue) NumberValue { return v / o } +func (v Word16Value) SaturatingDiv(_ NumberValue) NumberValue { + panic(errors.UnreachableError{}) +} + func (v Word16Value) Less(other NumberValue) BoolValue { return v < other.(Word16Value) } @@ -5631,24 +5174,7 @@ func (v Word16Value) BitwiseRightShift(other IntegerValue) IntegerValue { } func (v Word16Value) GetMember(_ *Interpreter, _ func() LocationRange, name string) Value { - switch name { - - case sema.ToStringFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - return NewStringValue(v.String()) - }, - ) - - case sema.ToBigEndianBytesFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - return ByteSliceToByteArrayValue(v.ToBigEndianBytes()) - }, - ) - } - - return nil + return getNumberValueMember(v, name) } func (Word16Value) SetMember(_ *Interpreter, _ func() LocationRange, _ string, _ Value) { @@ -5725,10 +5251,18 @@ func (v Word32Value) Plus(other NumberValue) NumberValue { return v + other.(Word32Value) } +func (v Word32Value) SaturatingPlus(_ NumberValue) NumberValue { + panic(errors.UnreachableError{}) +} + func (v Word32Value) Minus(other NumberValue) NumberValue { return v - other.(Word32Value) } +func (v Word32Value) SaturatingMinus(_ NumberValue) NumberValue { + panic(errors.UnreachableError{}) +} + func (v Word32Value) Mod(other NumberValue) NumberValue { o := other.(Word32Value) if o == 0 { @@ -5741,6 +5275,10 @@ func (v Word32Value) Mul(other NumberValue) NumberValue { return v * other.(Word32Value) } +func (v Word32Value) SaturatingMul(_ NumberValue) NumberValue { + panic(errors.UnreachableError{}) +} + func (v Word32Value) Div(other NumberValue) NumberValue { o := other.(Word32Value) if o == 0 { @@ -5749,6 +5287,10 @@ func (v Word32Value) Div(other NumberValue) NumberValue { return v / o } +func (v Word32Value) SaturatingDiv(_ NumberValue) NumberValue { + panic(errors.UnreachableError{}) +} + func (v Word32Value) Less(other NumberValue) BoolValue { return v < other.(Word32Value) } @@ -5803,24 +5345,7 @@ func (v Word32Value) BitwiseRightShift(other IntegerValue) IntegerValue { } func (v Word32Value) GetMember(_ *Interpreter, _ func() LocationRange, name string) Value { - switch name { - - case sema.ToStringFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - return NewStringValue(v.String()) - }, - ) - - case sema.ToBigEndianBytesFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - return ByteSliceToByteArrayValue(v.ToBigEndianBytes()) - }, - ) - } - - return nil + return getNumberValueMember(v, name) } func (Word32Value) SetMember(_ *Interpreter, _ func() LocationRange, _ string, _ Value) { @@ -5897,10 +5422,18 @@ func (v Word64Value) Plus(other NumberValue) NumberValue { return v + other.(Word64Value) } +func (v Word64Value) SaturatingPlus(_ NumberValue) NumberValue { + panic(errors.UnreachableError{}) +} + func (v Word64Value) Minus(other NumberValue) NumberValue { return v - other.(Word64Value) } +func (v Word64Value) SaturatingMinus(_ NumberValue) NumberValue { + panic(errors.UnreachableError{}) +} + func (v Word64Value) Mod(other NumberValue) NumberValue { o := other.(Word64Value) if o == 0 { @@ -5913,6 +5446,10 @@ func (v Word64Value) Mul(other NumberValue) NumberValue { return v * other.(Word64Value) } +func (v Word64Value) SaturatingMul(_ NumberValue) NumberValue { + panic(errors.UnreachableError{}) +} + func (v Word64Value) Div(other NumberValue) NumberValue { o := other.(Word64Value) if o == 0 { @@ -5921,6 +5458,10 @@ func (v Word64Value) Div(other NumberValue) NumberValue { return v / o } +func (v Word64Value) SaturatingDiv(_ NumberValue) NumberValue { + panic(errors.UnreachableError{}) +} + func (v Word64Value) Less(other NumberValue) BoolValue { return v < other.(Word64Value) } @@ -5975,24 +5516,7 @@ func (v Word64Value) BitwiseRightShift(other IntegerValue) IntegerValue { } func (v Word64Value) GetMember(_ *Interpreter, _ func() LocationRange, name string) Value { - switch name { - - case sema.ToStringFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - return NewStringValue(v.String()) - }, - ) - - case sema.ToBigEndianBytesFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - return ByteSliceToByteArrayValue(v.ToBigEndianBytes()) - }, - ) - } - - return nil + return getNumberValueMember(v, name) } func (Word64Value) SetMember(_ *Interpreter, _ func() LocationRange, _ string, _ Value) { @@ -6265,56 +5789,7 @@ func ConvertFix64(value Value) Fix64Value { } func (v Fix64Value) GetMember(_ *Interpreter, _ func() LocationRange, name string) Value { - switch name { - - case sema.ToStringFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - return NewStringValue(v.String()) - }, - ) - - case sema.ToBigEndianBytesFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - return ByteSliceToByteArrayValue(v.ToBigEndianBytes()) - }, - ) - - case sema.NumericTypeSaturatingAddFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - other := invocation.Arguments[0].(NumberValue) - return v.SaturatingPlus(other) - }, - ) - - case sema.NumericTypeSaturatingSubtractFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - other := invocation.Arguments[0].(NumberValue) - return v.SaturatingMinus(other) - }, - ) - - case sema.NumericTypeSaturatingMultiplyFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - other := invocation.Arguments[0].(NumberValue) - return v.SaturatingMul(other) - }, - ) - - case sema.NumericTypeSaturatingDivideFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - other := invocation.Arguments[0].(NumberValue) - return v.SaturatingDiv(other) - }, - ) - } - - return nil + return getNumberValueMember(v, name) } func (Fix64Value) SetMember(_ *Interpreter, _ func() LocationRange, _ string, _ Value) { @@ -6474,6 +5949,10 @@ func (v UFix64Value) Div(other NumberValue) NumberValue { return UFix64Value(result.Uint64()) } +func (v UFix64Value) SaturatingDiv(other NumberValue) NumberValue { + return v.Div(other) +} + func (v UFix64Value) Mod(other NumberValue) NumberValue { o := other.(UFix64Value) // v - int(v/o) * o @@ -6549,48 +6028,7 @@ func ConvertUFix64(value Value) UFix64Value { } func (v UFix64Value) GetMember(_ *Interpreter, _ func() LocationRange, name string) Value { - switch name { - - case sema.ToStringFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - return NewStringValue(v.String()) - }, - ) - - case sema.ToBigEndianBytesFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - return ByteSliceToByteArrayValue(v.ToBigEndianBytes()) - }, - ) - - case sema.NumericTypeSaturatingAddFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - other := invocation.Arguments[0].(NumberValue) - return v.SaturatingPlus(other) - }, - ) - - case sema.NumericTypeSaturatingSubtractFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - other := invocation.Arguments[0].(NumberValue) - return v.SaturatingMinus(other) - }, - ) - - case sema.NumericTypeSaturatingMultiplyFunctionName: - return NewHostFunctionValue( - func(invocation Invocation) Value { - other := invocation.Arguments[0].(NumberValue) - return v.SaturatingMul(other) - }, - ) - } - - return nil + return getNumberValueMember(v, name) } func (UFix64Value) SetMember(_ *Interpreter, _ func() LocationRange, _ string, _ Value) {