diff --git a/src/Neo.VM/Types/CompoundType.cs b/src/Neo.VM/Types/CompoundType.cs
index 6b0da819ec..33f670bfcb 100644
--- a/src/Neo.VM/Types/CompoundType.cs
+++ b/src/Neo.VM/Types/CompoundType.cs
@@ -60,12 +60,32 @@ public sealed override bool GetBoolean()
}
///
- /// The operation is not supported. Always throw .
+ ///
+ /// This method provides a hash code for the based on its item's span.
+ /// It is used for efficient storage and retrieval in hash-based collections.
+ ///
+ /// Use this method when you need a hash code for a .
///
- /// This method always throws the exception.
+ /// The hash code for the .
public override int GetHashCode()
{
- throw new NotSupportedException();
+ var h = new HashCode();
+ h.Add(Count);
+ h.Add(Type);
+ foreach (var item in SubItems)
+ {
+ // This isn't prefect and leaves somethings unsolved.
+ if (item is CompoundType cItem)
+ {
+ h.Add(cItem.Count);
+ h.Add(cItem.Type);
+ }
+ else
+ {
+ h.Add(item.GetHashCode());
+ }
+ }
+ return h.ToHashCode();
}
public override string ToString()
diff --git a/tests/Neo.VM.Tests/UT_StackItem.cs b/tests/Neo.VM.Tests/UT_StackItem.cs
index 6982f2463a..61f6de74a5 100644
--- a/tests/Neo.VM.Tests/UT_StackItem.cs
+++ b/tests/Neo.VM.Tests/UT_StackItem.cs
@@ -19,6 +19,21 @@ namespace Neo.Test
[TestClass]
public class UT_StackItem
{
+ [TestMethod]
+ public void TestCircularReference()
+ {
+ var itemA = new Struct { true, false };
+ var itemB = new Struct { true, false };
+ var itemC = new Struct { false, false };
+
+ itemA[1] = itemA;
+ itemB[1] = itemB;
+ itemC[1] = itemC;
+
+ Assert.AreEqual(itemA.GetHashCode(), itemB.GetHashCode());
+ Assert.AreNotEqual(itemA.GetHashCode(), itemC.GetHashCode());
+ }
+
[TestMethod]
public void TestHashCode()
{
@@ -62,17 +77,35 @@ public void TestHashCode()
Assert.IsTrue(itemA.GetHashCode() == itemB.GetHashCode());
- itemA = new VM.Types.Array();
+ itemA = new Array { true, false, 0 };
+ itemB = new Array { true, false, 0 };
+ itemC = new Array { true, false, 1 };
+
+ Assert.IsTrue(itemA.GetHashCode() == itemB.GetHashCode());
+ Assert.IsTrue(itemA.GetHashCode() != itemC.GetHashCode());
+
+ itemA = new Struct { true, false, 0 };
+ itemB = new Struct { true, false, 0 };
+ itemC = new Struct { true, false, 1 };
- Assert.ThrowsException(() => itemA.GetHashCode());
+ Assert.IsTrue(itemA.GetHashCode() == itemB.GetHashCode());
+ Assert.IsTrue(itemA.GetHashCode() != itemC.GetHashCode());
- itemA = new Struct();
+ itemA = new Map { [true] = false, [0] = 1 };
+ itemB = new Map { [true] = false, [0] = 1 };
+ itemC = new Map { [true] = false, [0] = 2 };
- Assert.ThrowsException(() => itemA.GetHashCode());
+ Assert.IsTrue(itemA.GetHashCode() == itemB.GetHashCode());
+ Assert.IsTrue(itemA.GetHashCode() != itemC.GetHashCode());
- itemA = new Map();
+ // Test CompoundType GetHashCode for subitems
+ var junk = new Array { true, false, 0 };
+ itemA = new Map { [true] = junk, [0] = junk };
+ itemB = new Map { [true] = junk, [0] = junk };
+ itemC = new Map { [true] = junk, [0] = 2 };
- Assert.ThrowsException(() => itemA.GetHashCode());
+ Assert.IsTrue(itemA.GetHashCode() == itemB.GetHashCode());
+ Assert.IsTrue(itemA.GetHashCode() != itemC.GetHashCode());
itemA = new InteropInterface(123);
itemB = new InteropInterface(123);