From 2f299785a8570d0a5d83d9f486b3b9f4d845b108 Mon Sep 17 00:00:00 2001 From: nan01ab Date: Sun, 19 Jan 2025 23:58:34 +0800 Subject: [PATCH 1/2] fix: check(-x) if x is int or long --- .../MethodConvert/Expression/Expression.cs | 4 + .../Expression/UnaryExpression.PrefixUnary.cs | 37 ++++++- .../Contract_Overflow.cs | 9 ++ .../TestingArtifacts/Contract_Overflow.cs | 98 ++++++++++++++++++- .../UnitTest_Overflow.cs | 26 ++++- 5 files changed, 170 insertions(+), 4 deletions(-) diff --git a/src/Neo.Compiler.CSharp/MethodConvert/Expression/Expression.cs b/src/Neo.Compiler.CSharp/MethodConvert/Expression/Expression.cs index f4819a790..a827e492d 100644 --- a/src/Neo.Compiler.CSharp/MethodConvert/Expression/Expression.cs +++ b/src/Neo.Compiler.CSharp/MethodConvert/Expression/Expression.cs @@ -266,8 +266,11 @@ private void EnsureIntegerInRange(ITypeSymbol type) { if (type.Name == "BigInteger") return; while (type.NullableAnnotation == NullableAnnotation.Annotated) + { // Supporting nullable integer like `byte?` type = ((INamedTypeSymbol)type).TypeArguments.First(); + } + var (minValue, maxValue, mask) = type.Name switch { "SByte" => ((BigInteger)sbyte.MinValue, (BigInteger)sbyte.MaxValue, (BigInteger)0xff), @@ -282,6 +285,7 @@ private void EnsureIntegerInRange(ITypeSymbol type) //"Boolean" => (0, 1, 0x01), _ => throw new CompilationException(DiagnosticId.SyntaxNotSupported, $"Unsupported type: {type}") }; + JumpTarget checkUpperBoundTarget = new(), adjustTarget = new(), endTarget = new(); AddInstruction(OpCode.DUP); Push(minValue); diff --git a/src/Neo.Compiler.CSharp/MethodConvert/Expression/UnaryExpression.PrefixUnary.cs b/src/Neo.Compiler.CSharp/MethodConvert/Expression/UnaryExpression.PrefixUnary.cs index b0c877fb5..5149385b4 100644 --- a/src/Neo.Compiler.CSharp/MethodConvert/Expression/UnaryExpression.PrefixUnary.cs +++ b/src/Neo.Compiler.CSharp/MethodConvert/Expression/UnaryExpression.PrefixUnary.cs @@ -16,6 +16,7 @@ using Neo.VM; using System; using System.Runtime.InteropServices; +using System.Linq; namespace Neo.Compiler; @@ -46,7 +47,7 @@ private void ConvertPrefixUnaryExpression(SemanticModel model, PrefixUnaryExpres break; case "-": ConvertExpression(model, expression.Operand); - AddInstruction(OpCode.NEGATE); + EmitNegativeInteger(model.GetTypeInfo(expression.Operand).Type); break; case "~": ConvertExpression(model, expression.Operand); @@ -274,4 +275,38 @@ private void EmitIncrementOrDecrement(SyntaxToken operatorToken, ITypeSymbol? ty }); if (typeSymbol != null) EnsureIntegerInRange(typeSymbol); } + + private void EmitNegativeInteger(ITypeSymbol? typeSymbol) + { + if (typeSymbol is null || (typeSymbol.Name != "Int32" && typeSymbol.Name != "Int64")) + { + // -sbyte, -byte, -short, -ushort, -char -> int, -int, -uint -> long + AddInstruction(OpCode.NEGATE); // Emit NEGATE for other integer types + return; + } + + while (typeSymbol.NullableAnnotation == NullableAnnotation.Annotated) + { + // Supporting nullable integer like `byte?` + typeSymbol = ((INamedTypeSymbol)typeSymbol).TypeArguments.First(); + } + + var minValue = typeSymbol.Name == "Int64" ? long.MinValue : int.MinValue; // int32 or int64 + + JumpTarget negateTarget = new(), endTarget = new(); + AddInstruction(OpCode.DUP); + Push(minValue); + Jump(OpCode.JMPNE_L, negateTarget); + + if (_checkedStack.Peek()) // if `checked` is true, throw exception + { + AddInstruction(OpCode.THROW); + } + else // -int.MinValue == -int.MinValue, -long.MinValue == -long.MinValue, i.e. same value + { + Jump(endTarget); + } + negateTarget.Instruction = AddInstruction(OpCode.NEGATE); + endTarget.Instruction = AddInstruction(OpCode.NOP); + } } diff --git a/tests/Neo.Compiler.CSharp.TestContracts/Contract_Overflow.cs b/tests/Neo.Compiler.CSharp.TestContracts/Contract_Overflow.cs index c012d8d6d..42f20052e 100644 --- a/tests/Neo.Compiler.CSharp.TestContracts/Contract_Overflow.cs +++ b/tests/Neo.Compiler.CSharp.TestContracts/Contract_Overflow.cs @@ -6,5 +6,14 @@ public class Contract_Overflow : SmartContract.Framework.SmartContract public static int MulInt(int a, int b) => a * b; public static uint AddUInt(uint a, uint b) => a + b; public static uint MulUInt(uint a, uint b) => a * b; + + public static int NegateIntChecked(int a) => checked(-a); + public static int NegateInt(int a) => -a; + + public static long NegateLongChecked(long a) => checked(-a); + public static long NegateLong(long a) => -a; + + public static int NegateShortChecked(short a) => checked(-a); + public static int NegateShort(short a) => -a; } } diff --git a/tests/Neo.Compiler.CSharp.UnitTests/TestingArtifacts/Contract_Overflow.cs b/tests/Neo.Compiler.CSharp.UnitTests/TestingArtifacts/Contract_Overflow.cs index 2c9ec9aaa..326b2aa9b 100644 --- a/tests/Neo.Compiler.CSharp.UnitTests/TestingArtifacts/Contract_Overflow.cs +++ b/tests/Neo.Compiler.CSharp.UnitTests/TestingArtifacts/Contract_Overflow.cs @@ -10,12 +10,12 @@ public abstract class Contract_Overflow(Neo.SmartContract.Testing.SmartContractI { #region Compiled data - public static Neo.SmartContract.Manifest.ContractManifest Manifest => Neo.SmartContract.Manifest.ContractManifest.Parse(@"{""name"":""Contract_Overflow"",""groups"":[],""features"":{},""supportedstandards"":[],""abi"":{""methods"":[{""name"":""addInt"",""parameters"":[{""name"":""a"",""type"":""Integer""},{""name"":""b"",""type"":""Integer""}],""returntype"":""Integer"",""offset"":0,""safe"":false},{""name"":""mulInt"",""parameters"":[{""name"":""a"",""type"":""Integer""},{""name"":""b"",""type"":""Integer""}],""returntype"":""Integer"",""offset"":53,""safe"":false},{""name"":""addUInt"",""parameters"":[{""name"":""a"",""type"":""Integer""},{""name"":""b"",""type"":""Integer""}],""returntype"":""Integer"",""offset"":106,""safe"":false},{""name"":""mulUInt"",""parameters"":[{""name"":""a"",""type"":""Integer""},{""name"":""b"",""type"":""Integer""}],""returntype"":""Integer"",""offset"":141,""safe"":false}],""events"":[]},""permissions"":[],""trusts"":[],""extra"":{""nef"":{""optimization"":""All""}}}"); + public static Neo.SmartContract.Manifest.ContractManifest Manifest => Neo.SmartContract.Manifest.ContractManifest.Parse(@"{""name"":""Contract_Overflow"",""groups"":[],""features"":{},""supportedstandards"":[],""abi"":{""methods"":[{""name"":""addInt"",""parameters"":[{""name"":""a"",""type"":""Integer""},{""name"":""b"",""type"":""Integer""}],""returntype"":""Integer"",""offset"":0,""safe"":false},{""name"":""mulInt"",""parameters"":[{""name"":""a"",""type"":""Integer""},{""name"":""b"",""type"":""Integer""}],""returntype"":""Integer"",""offset"":53,""safe"":false},{""name"":""addUInt"",""parameters"":[{""name"":""a"",""type"":""Integer""},{""name"":""b"",""type"":""Integer""}],""returntype"":""Integer"",""offset"":106,""safe"":false},{""name"":""mulUInt"",""parameters"":[{""name"":""a"",""type"":""Integer""},{""name"":""b"",""type"":""Integer""}],""returntype"":""Integer"",""offset"":141,""safe"":false},{""name"":""negateIntChecked"",""parameters"":[{""name"":""a"",""type"":""Integer""}],""returntype"":""Integer"",""offset"":176,""safe"":false},{""name"":""negateInt"",""parameters"":[{""name"":""a"",""type"":""Integer""}],""returntype"":""Integer"",""offset"":191,""safe"":false},{""name"":""negateLongChecked"",""parameters"":[{""name"":""a"",""type"":""Integer""}],""returntype"":""Integer"",""offset"":206,""safe"":false},{""name"":""negateLong"",""parameters"":[{""name"":""a"",""type"":""Integer""}],""returntype"":""Integer"",""offset"":225,""safe"":false},{""name"":""negateShortChecked"",""parameters"":[{""name"":""a"",""type"":""Integer""}],""returntype"":""Integer"",""offset"":244,""safe"":false},{""name"":""negateShort"",""parameters"":[{""name"":""a"",""type"":""Integer""}],""returntype"":""Integer"",""offset"":250,""safe"":false}],""events"":[]},""permissions"":[],""trusts"":[],""extra"":{""nef"":{""optimization"":""All""}}}"); /// /// Optimization: "All" /// - public static Neo.SmartContract.NefFile Nef => Neo.IO.Helper.AsSerializable(Convert.FromBase64String(@"TkVGM1Rlc3RpbmdFbmdpbmUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALBXAAJ4eZ5KAgAAAIAuBCIKSgL///9/Mh4D/////wAAAACRSgL///9/MgwDAAAAAAEAAACfQFcAAnh5oEoCAAAAgC4EIgpKAv///38yHgP/////AAAAAJFKAv///38yDAMAAAAAAQAAAJ9AVwACeHmeShAuBCIOSgP/////AAAAADIMA/////8AAAAAkUBXAAJ4eaBKEC4EIg5KA/////8AAAAAMgwD/////wAAAACRQFd1rUY=")); + public static Neo.SmartContract.NefFile Nef => Neo.IO.Helper.AsSerializable(Convert.FromBase64String(@"TkVGM1Rlc3RpbmdFbmdpbmUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP0AAVcAAnh5nkoCAAAAgC4EIgpKAv///38yHgP/////AAAAAJFKAv///38yDAMAAAAAAQAAAJ9AVwACeHmgSgIAAACALgQiCkoC////fzIeA/////8AAAAAkUoC////fzIMAwAAAAABAAAAn0BXAAJ4eZ5KEC4EIg5KA/////8AAAAAMgwD/////wAAAACRQFcAAnh5oEoQLgQiDkoD/////wAAAAAyDAP/////AAAAAJFAVwABeEoCAAAAgCoDOptAVwABeEoCAAAAgCoDQJtAVwABeEoDAAAAAAAAAIAqAzqbQFcAAXhKAwAAAAAAAACAKgNAm0BXAAF4m0BXAAF4m0CeMqEq")); #endregion @@ -123,5 +123,99 @@ public abstract class Contract_Overflow(Neo.SmartContract.Testing.SmartContractI [DisplayName("mulUInt")] public abstract BigInteger? MulUInt(BigInteger? a, BigInteger? b); + /// + /// Unsafe method + /// + /// + /// Script: VwABeEoCAAAAgCoDQJtA + /// INITSLOT 0001 [64 datoshi] + /// LDARG0 [2 datoshi] + /// DUP [2 datoshi] + /// PUSHINT32 00000080 [1 datoshi] + /// JMPNE 03 [2 datoshi] + /// RET [0 datoshi] + /// NEGATE [4 datoshi] + /// RET [0 datoshi] + /// + [DisplayName("negateInt")] + public abstract BigInteger? NegateInt(BigInteger? a); + + /// + /// Unsafe method + /// + /// + /// Script: VwABeEoCAAAAgCoDOptA + /// INITSLOT 0001 [64 datoshi] + /// LDARG0 [2 datoshi] + /// DUP [2 datoshi] + /// PUSHINT32 00000080 [1 datoshi] + /// JMPNE 03 [2 datoshi] + /// THROW [512 datoshi] + /// NEGATE [4 datoshi] + /// RET [0 datoshi] + /// + [DisplayName("negateIntChecked")] + public abstract BigInteger? NegateIntChecked(BigInteger? a); + + /// + /// Unsafe method + /// + /// + /// Script: VwABeEoDAAAAAAAAAIAqA0CbQA== + /// INITSLOT 0001 [64 datoshi] + /// LDARG0 [2 datoshi] + /// DUP [2 datoshi] + /// PUSHINT64 0000000000000080 [1 datoshi] + /// JMPNE 03 [2 datoshi] + /// RET [0 datoshi] + /// NEGATE [4 datoshi] + /// RET [0 datoshi] + /// + [DisplayName("negateLong")] + public abstract BigInteger? NegateLong(BigInteger? a); + + /// + /// Unsafe method + /// + /// + /// Script: VwABeEoDAAAAAAAAAIAqAzqbQA== + /// INITSLOT 0001 [64 datoshi] + /// LDARG0 [2 datoshi] + /// DUP [2 datoshi] + /// PUSHINT64 0000000000000080 [1 datoshi] + /// JMPNE 03 [2 datoshi] + /// THROW [512 datoshi] + /// NEGATE [4 datoshi] + /// RET [0 datoshi] + /// + [DisplayName("negateLongChecked")] + public abstract BigInteger? NegateLongChecked(BigInteger? a); + + /// + /// Unsafe method + /// + /// + /// Script: VwABeJtA + /// INITSLOT 0001 [64 datoshi] + /// LDARG0 [2 datoshi] + /// NEGATE [4 datoshi] + /// RET [0 datoshi] + /// + [DisplayName("negateShort")] + public abstract BigInteger? NegateShort(BigInteger? a); + + /// + /// Unsafe method + /// + /// + /// Script: VwABeJtA + /// INITSLOT 0001 [64 datoshi] + /// LDARG0 [2 datoshi] + /// NEGATE [4 datoshi] + /// RET [0 datoshi] + /// + [DisplayName("negateShortChecked")] + public abstract BigInteger? NegateShortChecked(BigInteger? a); + #endregion } diff --git a/tests/Neo.Compiler.CSharp.UnitTests/UnitTest_Overflow.cs b/tests/Neo.Compiler.CSharp.UnitTests/UnitTest_Overflow.cs index 2e63cfe29..27169674f 100644 --- a/tests/Neo.Compiler.CSharp.UnitTests/UnitTest_Overflow.cs +++ b/tests/Neo.Compiler.CSharp.UnitTests/UnitTest_Overflow.cs @@ -1,7 +1,6 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.SmartContract.Testing; using Neo.SmartContract.Testing.Exceptions; -using System.Numerics; namespace Neo.Compiler.CSharp.UnitTests { @@ -49,5 +48,30 @@ public void Test_MulUInt() Assert.AreEqual(unchecked(uint.MinValue * (-uint.MaxValue)), Contract.MulUInt(uint.MinValue, -uint.MaxValue)); Assert.AreEqual(unchecked((-uint.MinValue) * uint.MaxValue), Contract.MulUInt(unchecked(-uint.MinValue), uint.MaxValue)); } + + [TestMethod] + public void Test_NegateChecked() + { + Assert.AreEqual(-2147483647, Contract.NegateIntChecked(2147483647)); + + // VMUnhandledException -int.MinValue + Assert.ThrowsException(() => Contract.NegateIntChecked(int.MinValue)); + + Assert.AreEqual(-9223372036854775807, Contract.NegateLongChecked(9223372036854775807)); + Assert.ThrowsException(() => Contract.NegateLongChecked(long.MinValue)); + + // -short -> int + Assert.AreEqual(-32767, Contract.NegateShortChecked(32767)); + Assert.AreEqual(32768, Contract.NegateShort(short.MinValue)); + + // unchecked(-int.MinValue) == int.MinValue + Assert.AreEqual(int.MinValue, unchecked(-int.MinValue)); + + // unchecked(-long.MinValue) == long.MinValue + Assert.AreEqual(long.MinValue, unchecked(-long.MinValue)); + + // it is different for short.MinValue, because `-short` is an int + Assert.AreEqual(32768, unchecked(-short.MinValue)); + } } } From 58b274c0377d0da8d243d5762fb8fdcd5dc259f1 Mon Sep 17 00:00:00 2001 From: nan01ab Date: Mon, 20 Jan 2025 00:29:06 +0800 Subject: [PATCH 2/2] fix: add more tests --- .../Contract_Overflow.cs | 6 + .../TestingArtifacts/Contract_Overflow.cs | 124 +++++++++++++++++- .../UnitTest_Overflow.cs | 22 +++- 3 files changed, 148 insertions(+), 4 deletions(-) diff --git a/tests/Neo.Compiler.CSharp.TestContracts/Contract_Overflow.cs b/tests/Neo.Compiler.CSharp.TestContracts/Contract_Overflow.cs index 42f20052e..d8539831f 100644 --- a/tests/Neo.Compiler.CSharp.TestContracts/Contract_Overflow.cs +++ b/tests/Neo.Compiler.CSharp.TestContracts/Contract_Overflow.cs @@ -15,5 +15,11 @@ public class Contract_Overflow : SmartContract.Framework.SmartContract public static int NegateShortChecked(short a) => checked(-a); public static int NegateShort(short a) => -a; + + public static int NegateAddInt(int a, int b) => -(a + b); + public static int NegateAddIntChecked(int a, int b) => checked(-(a + b)); + + public static long NegateAddLong(long a, long b) => -(a + b); + public static long NegateAddLongChecked(long a, long b) => checked(-(a + b)); } } diff --git a/tests/Neo.Compiler.CSharp.UnitTests/TestingArtifacts/Contract_Overflow.cs b/tests/Neo.Compiler.CSharp.UnitTests/TestingArtifacts/Contract_Overflow.cs index 326b2aa9b..2bc5d398c 100644 --- a/tests/Neo.Compiler.CSharp.UnitTests/TestingArtifacts/Contract_Overflow.cs +++ b/tests/Neo.Compiler.CSharp.UnitTests/TestingArtifacts/Contract_Overflow.cs @@ -10,12 +10,12 @@ public abstract class Contract_Overflow(Neo.SmartContract.Testing.SmartContractI { #region Compiled data - public static Neo.SmartContract.Manifest.ContractManifest Manifest => Neo.SmartContract.Manifest.ContractManifest.Parse(@"{""name"":""Contract_Overflow"",""groups"":[],""features"":{},""supportedstandards"":[],""abi"":{""methods"":[{""name"":""addInt"",""parameters"":[{""name"":""a"",""type"":""Integer""},{""name"":""b"",""type"":""Integer""}],""returntype"":""Integer"",""offset"":0,""safe"":false},{""name"":""mulInt"",""parameters"":[{""name"":""a"",""type"":""Integer""},{""name"":""b"",""type"":""Integer""}],""returntype"":""Integer"",""offset"":53,""safe"":false},{""name"":""addUInt"",""parameters"":[{""name"":""a"",""type"":""Integer""},{""name"":""b"",""type"":""Integer""}],""returntype"":""Integer"",""offset"":106,""safe"":false},{""name"":""mulUInt"",""parameters"":[{""name"":""a"",""type"":""Integer""},{""name"":""b"",""type"":""Integer""}],""returntype"":""Integer"",""offset"":141,""safe"":false},{""name"":""negateIntChecked"",""parameters"":[{""name"":""a"",""type"":""Integer""}],""returntype"":""Integer"",""offset"":176,""safe"":false},{""name"":""negateInt"",""parameters"":[{""name"":""a"",""type"":""Integer""}],""returntype"":""Integer"",""offset"":191,""safe"":false},{""name"":""negateLongChecked"",""parameters"":[{""name"":""a"",""type"":""Integer""}],""returntype"":""Integer"",""offset"":206,""safe"":false},{""name"":""negateLong"",""parameters"":[{""name"":""a"",""type"":""Integer""}],""returntype"":""Integer"",""offset"":225,""safe"":false},{""name"":""negateShortChecked"",""parameters"":[{""name"":""a"",""type"":""Integer""}],""returntype"":""Integer"",""offset"":244,""safe"":false},{""name"":""negateShort"",""parameters"":[{""name"":""a"",""type"":""Integer""}],""returntype"":""Integer"",""offset"":250,""safe"":false}],""events"":[]},""permissions"":[],""trusts"":[],""extra"":{""nef"":{""optimization"":""All""}}}"); + public static Neo.SmartContract.Manifest.ContractManifest Manifest => Neo.SmartContract.Manifest.ContractManifest.Parse(@"{""name"":""Contract_Overflow"",""groups"":[],""features"":{},""supportedstandards"":[],""abi"":{""methods"":[{""name"":""addInt"",""parameters"":[{""name"":""a"",""type"":""Integer""},{""name"":""b"",""type"":""Integer""}],""returntype"":""Integer"",""offset"":0,""safe"":false},{""name"":""mulInt"",""parameters"":[{""name"":""a"",""type"":""Integer""},{""name"":""b"",""type"":""Integer""}],""returntype"":""Integer"",""offset"":53,""safe"":false},{""name"":""addUInt"",""parameters"":[{""name"":""a"",""type"":""Integer""},{""name"":""b"",""type"":""Integer""}],""returntype"":""Integer"",""offset"":106,""safe"":false},{""name"":""mulUInt"",""parameters"":[{""name"":""a"",""type"":""Integer""},{""name"":""b"",""type"":""Integer""}],""returntype"":""Integer"",""offset"":141,""safe"":false},{""name"":""negateIntChecked"",""parameters"":[{""name"":""a"",""type"":""Integer""}],""returntype"":""Integer"",""offset"":176,""safe"":false},{""name"":""negateInt"",""parameters"":[{""name"":""a"",""type"":""Integer""}],""returntype"":""Integer"",""offset"":191,""safe"":false},{""name"":""negateLongChecked"",""parameters"":[{""name"":""a"",""type"":""Integer""}],""returntype"":""Integer"",""offset"":206,""safe"":false},{""name"":""negateLong"",""parameters"":[{""name"":""a"",""type"":""Integer""}],""returntype"":""Integer"",""offset"":225,""safe"":false},{""name"":""negateShortChecked"",""parameters"":[{""name"":""a"",""type"":""Integer""}],""returntype"":""Integer"",""offset"":244,""safe"":false},{""name"":""negateShort"",""parameters"":[{""name"":""a"",""type"":""Integer""}],""returntype"":""Integer"",""offset"":250,""safe"":false},{""name"":""negateAddInt"",""parameters"":[{""name"":""a"",""type"":""Integer""},{""name"":""b"",""type"":""Integer""}],""returntype"":""Integer"",""offset"":256,""safe"":false},{""name"":""negateAddIntChecked"",""parameters"":[{""name"":""a"",""type"":""Integer""},{""name"":""b"",""type"":""Integer""}],""returntype"":""Integer"",""offset"":319,""safe"":false},{""name"":""negateAddLong"",""parameters"":[{""name"":""a"",""type"":""Integer""},{""name"":""b"",""type"":""Integer""}],""returntype"":""Integer"",""offset"":354,""safe"":false},{""name"":""negateAddLongChecked"",""parameters"":[{""name"":""a"",""type"":""Integer""},{""name"":""b"",""type"":""Integer""}],""returntype"":""Integer"",""offset"":449,""safe"":false}],""events"":[]},""permissions"":[],""trusts"":[],""extra"":{""nef"":{""optimization"":""All""}}}"); /// /// Optimization: "All" /// - public static Neo.SmartContract.NefFile Nef => Neo.IO.Helper.AsSerializable(Convert.FromBase64String(@"TkVGM1Rlc3RpbmdFbmdpbmUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP0AAVcAAnh5nkoCAAAAgC4EIgpKAv///38yHgP/////AAAAAJFKAv///38yDAMAAAAAAQAAAJ9AVwACeHmgSgIAAACALgQiCkoC////fzIeA/////8AAAAAkUoC////fzIMAwAAAAABAAAAn0BXAAJ4eZ5KEC4EIg5KA/////8AAAAAMgwD/////wAAAACRQFcAAnh5oEoQLgQiDkoD/////wAAAAAyDAP/////AAAAAJFAVwABeEoCAAAAgCoDOptAVwABeEoCAAAAgCoDQJtAVwABeEoDAAAAAAAAAIAqAzqbQFcAAXhKAwAAAAAAAACAKgNAm0BXAAF4m0BXAAF4m0CeMqEq")); + public static Neo.SmartContract.NefFile Nef => Neo.IO.Helper.AsSerializable(Convert.FromBase64String(@"TkVGM1Rlc3RpbmdFbmdpbmUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP3wAVcAAnh5nkoCAAAAgC4EIgpKAv///38yHgP/////AAAAAJFKAv///38yDAMAAAAAAQAAAJ9AVwACeHmgSgIAAACALgQiCkoC////fzIeA/////8AAAAAkUoC////fzIMAwAAAAABAAAAn0BXAAJ4eZ5KEC4EIg5KA/////8AAAAAMgwD/////wAAAACRQFcAAnh5oEoQLgQiDkoD/////wAAAAAyDAP/////AAAAAJFAVwABeEoCAAAAgCoDOptAVwABeEoCAAAAgCoDQJtAVwABeEoDAAAAAAAAAIAqAzqbQFcAAXhKAwAAAAAAAACAKgNAm0BXAAF4m0BXAAF4m0BXAAJ4eZ5KAgAAAIAuBCIKSgL///9/Mh4D/////wAAAACRSgL///9/MgwDAAAAAAEAAACfSgIAAACAKgNAm0BXAAJ4eZ5KAgAAAIAuAzpKAv///38yAzpKAgAAAIAqAzqbQFcAAnh5nkoDAAAAAAAAAIAuBCIOSgP/////////fzIyBP//////////AAAAAAAAAACRSgP/////////fzIUBAAAAAAAAAAAAQAAAAAAAACfSgMAAAAAAAAAgCoDQJtAVwACeHmeSgMAAAAAAAAAgC4DOkoD/////////38yAzpKAwAAAAAAAACAKgM6m0D1Twbi")); #endregion @@ -123,6 +123,126 @@ public abstract class Contract_Overflow(Neo.SmartContract.Testing.SmartContractI [DisplayName("mulUInt")] public abstract BigInteger? MulUInt(BigInteger? a, BigInteger? b); + /// + /// Unsafe method + /// + /// + /// Script: VwACeHmeSgIAAACALgQiCkoC////fzIeA/////8AAAAAkUoC////fzIMAwAAAAABAAAAn0oCAAAAgCoDQJtA + /// INITSLOT 0002 [64 datoshi] + /// LDARG0 [2 datoshi] + /// LDARG1 [2 datoshi] + /// ADD [8 datoshi] + /// DUP [2 datoshi] + /// PUSHINT32 00000080 [1 datoshi] + /// JMPGE 04 [2 datoshi] + /// JMP 0A [2 datoshi] + /// DUP [2 datoshi] + /// PUSHINT32 FFFFFF7F [1 datoshi] + /// JMPLE 1E [2 datoshi] + /// PUSHINT64 FFFFFFFF00000000 [1 datoshi] + /// AND [8 datoshi] + /// DUP [2 datoshi] + /// PUSHINT32 FFFFFF7F [1 datoshi] + /// JMPLE 0C [2 datoshi] + /// PUSHINT64 0000000001000000 [1 datoshi] + /// SUB [8 datoshi] + /// DUP [2 datoshi] + /// PUSHINT32 00000080 [1 datoshi] + /// JMPNE 03 [2 datoshi] + /// RET [0 datoshi] + /// NEGATE [4 datoshi] + /// RET [0 datoshi] + /// + [DisplayName("negateAddInt")] + public abstract BigInteger? NegateAddInt(BigInteger? a, BigInteger? b); + + /// + /// Unsafe method + /// + /// + /// Script: VwACeHmeSgIAAACALgM6SgL///9/MgM6SgIAAACAKgM6m0A= + /// INITSLOT 0002 [64 datoshi] + /// LDARG0 [2 datoshi] + /// LDARG1 [2 datoshi] + /// ADD [8 datoshi] + /// DUP [2 datoshi] + /// PUSHINT32 00000080 [1 datoshi] + /// JMPGE 03 [2 datoshi] + /// THROW [512 datoshi] + /// DUP [2 datoshi] + /// PUSHINT32 FFFFFF7F [1 datoshi] + /// JMPLE 03 [2 datoshi] + /// THROW [512 datoshi] + /// DUP [2 datoshi] + /// PUSHINT32 00000080 [1 datoshi] + /// JMPNE 03 [2 datoshi] + /// THROW [512 datoshi] + /// NEGATE [4 datoshi] + /// RET [0 datoshi] + /// + [DisplayName("negateAddIntChecked")] + public abstract BigInteger? NegateAddIntChecked(BigInteger? a, BigInteger? b); + + /// + /// Unsafe method + /// + /// + /// Script: VwACeHmeSgMAAAAAAAAAgC4EIg5KA/////////9/MjIE//////////8AAAAAAAAAAJFKA/////////9/MhQEAAAAAAAAAAABAAAAAAAAAJ9KAwAAAAAAAACAKgNAm0A= + /// INITSLOT 0002 [64 datoshi] + /// LDARG0 [2 datoshi] + /// LDARG1 [2 datoshi] + /// ADD [8 datoshi] + /// DUP [2 datoshi] + /// PUSHINT64 0000000000000080 [1 datoshi] + /// JMPGE 04 [2 datoshi] + /// JMP 0E [2 datoshi] + /// DUP [2 datoshi] + /// PUSHINT64 FFFFFFFFFFFFFF7F [1 datoshi] + /// JMPLE 32 [2 datoshi] + /// PUSHINT128 FFFFFFFFFFFFFFFF0000000000000000 [4 datoshi] + /// AND [8 datoshi] + /// DUP [2 datoshi] + /// PUSHINT64 FFFFFFFFFFFFFF7F [1 datoshi] + /// JMPLE 14 [2 datoshi] + /// PUSHINT128 00000000000000000100000000000000 [4 datoshi] + /// SUB [8 datoshi] + /// DUP [2 datoshi] + /// PUSHINT64 0000000000000080 [1 datoshi] + /// JMPNE 03 [2 datoshi] + /// RET [0 datoshi] + /// NEGATE [4 datoshi] + /// RET [0 datoshi] + /// + [DisplayName("negateAddLong")] + public abstract BigInteger? NegateAddLong(BigInteger? a, BigInteger? b); + + /// + /// Unsafe method + /// + /// + /// Script: VwACeHmeSgMAAAAAAAAAgC4DOkoD/////////38yAzpKAwAAAAAAAACAKgM6m0A= + /// INITSLOT 0002 [64 datoshi] + /// LDARG0 [2 datoshi] + /// LDARG1 [2 datoshi] + /// ADD [8 datoshi] + /// DUP [2 datoshi] + /// PUSHINT64 0000000000000080 [1 datoshi] + /// JMPGE 03 [2 datoshi] + /// THROW [512 datoshi] + /// DUP [2 datoshi] + /// PUSHINT64 FFFFFFFFFFFFFF7F [1 datoshi] + /// JMPLE 03 [2 datoshi] + /// THROW [512 datoshi] + /// DUP [2 datoshi] + /// PUSHINT64 0000000000000080 [1 datoshi] + /// JMPNE 03 [2 datoshi] + /// THROW [512 datoshi] + /// NEGATE [4 datoshi] + /// RET [0 datoshi] + /// + [DisplayName("negateAddLongChecked")] + public abstract BigInteger? NegateAddLongChecked(BigInteger? a, BigInteger? b); + /// /// Unsafe method /// diff --git a/tests/Neo.Compiler.CSharp.UnitTests/UnitTest_Overflow.cs b/tests/Neo.Compiler.CSharp.UnitTests/UnitTest_Overflow.cs index 27169674f..551aebaad 100644 --- a/tests/Neo.Compiler.CSharp.UnitTests/UnitTest_Overflow.cs +++ b/tests/Neo.Compiler.CSharp.UnitTests/UnitTest_Overflow.cs @@ -52,12 +52,16 @@ public void Test_MulUInt() [TestMethod] public void Test_NegateChecked() { - Assert.AreEqual(-2147483647, Contract.NegateIntChecked(2147483647)); + Assert.AreEqual(-2147483647, Contract.NegateIntChecked(int.MaxValue)); + Assert.AreEqual(-2147483647, Contract.NegateInt(int.MaxValue)); // VMUnhandledException -int.MinValue Assert.ThrowsException(() => Contract.NegateIntChecked(int.MinValue)); - Assert.AreEqual(-9223372036854775807, Contract.NegateLongChecked(9223372036854775807)); + Assert.AreEqual(-long.MaxValue, Contract.NegateLongChecked(long.MaxValue)); + Assert.AreEqual(-long.MaxValue, Contract.NegateLong(long.MaxValue)); + + // VMUnhandledException -long.MinValue Assert.ThrowsException(() => Contract.NegateLongChecked(long.MinValue)); // -short -> int @@ -72,6 +76,20 @@ public void Test_NegateChecked() // it is different for short.MinValue, because `-short` is an int Assert.AreEqual(32768, unchecked(-short.MinValue)); + + + // add and negate + Assert.AreEqual(-2147483648, Contract.NegateAddInt(int.MaxValue, 1)); + Assert.ThrowsException(() => Contract.NegateAddIntChecked(int.MaxValue, 1)); + + Assert.AreEqual(-9223372036854775808, Contract.NegateAddLong(long.MaxValue, 1)); + Assert.ThrowsException(() => Contract.NegateAddLongChecked(long.MaxValue, 1)); + + Assert.AreEqual(-2147483648, Contract.NegateAddInt(-2147483647, -1)); + Assert.ThrowsException(() => Contract.NegateAddIntChecked(-2147483647, -1)); + + Assert.AreEqual(-9223372036854775808, Contract.NegateAddLong(-9223372036854775807, -1)); + Assert.ThrowsException(() => Contract.NegateAddLongChecked(-9223372036854775807, -1)); } } }