diff --git a/benchmarks/Neo.VM.Benchmarks/Benchmarks.cs b/benchmarks/Neo.VM.Benchmarks/Benchmarks.cs
new file mode 100644
index 0000000000..dd7df7d3d1
--- /dev/null
+++ b/benchmarks/Neo.VM.Benchmarks/Benchmarks.cs
@@ -0,0 +1,101 @@
+using System.Diagnostics;
+
+namespace Neo.VM
+{
+ public static class Benchmarks
+ {
+ public static void NeoIssue2528()
+ {
+ // https://github.com/neo-project/neo/issues/2528
+ // L01: INITSLOT 1, 0
+ // L02: NEWARRAY0
+ // L03: DUP
+ // L04: DUP
+ // L05: PUSHINT16 2043
+ // L06: STLOC 0
+ // L07: PUSH1
+ // L08: PACK
+ // L09: LDLOC 0
+ // L10: DEC
+ // L11: STLOC 0
+ // L12: LDLOC 0
+ // L13: JMPIF_L L07
+ // L14: PUSH1
+ // L15: PACK
+ // L16: APPEND
+ // L17: PUSHINT32 38000
+ // L18: STLOC 0
+ // L19: PUSH0
+ // L20: PICKITEM
+ // L21: LDLOC 0
+ // L22: DEC
+ // L23: STLOC 0
+ // L24: LDLOC 0
+ // L25: JMPIF_L L19
+ // L26: DROP
+ Run(nameof(NeoIssue2528), "VwEAwkpKAfsHdwARwG8AnXcAbwAl9////xHAzwJwlAAAdwAQzm8AnXcAbwAl9////0U=");
+ }
+
+ public static void NeoVMIssue418()
+ {
+ // https://github.com/neo-project/neo-vm/issues/418
+ // L00: NEWARRAY0
+ // L01: PUSH0
+ // L02: PICK
+ // L03: PUSH1
+ // L04: PACK
+ // L05: PUSH1
+ // L06: PICK
+ // L07: PUSH1
+ // L08: PACK
+ // L09: INITSSLOT 1
+ // L10: PUSHINT16 510
+ // L11: DEC
+ // L12: STSFLD0
+ // L13: PUSH1
+ // L14: PICK
+ // L15: PUSH1
+ // L16: PICK
+ // L17: PUSH2
+ // L18: PACK
+ // L19: REVERSE3
+ // L20: PUSH2
+ // L21: PACK
+ // L22: LDSFLD0
+ // L23: DUP
+ // L24: JMPIF L11
+ // L25: DROP
+ // L26: ROT
+ // L27: DROP
+ Run(nameof(NeoVMIssue418), "whBNEcARTRHAVgEB/gGdYBFNEU0SwFMSwFhKJPNFUUU=");
+ }
+
+ public static void NeoIssue2723()
+ {
+ // L00: INITSSLOT 1
+ // L01: PUSHINT32 130000
+ // L02: STSFLD 0
+ // L03: PUSHINT32 1048576
+ // L04: NEWBUFFER
+ // L05: DROP
+ // L06: LDSFLD 0
+ // L07: DEC
+ // L08: DUP
+ // L09: STSFLD 0
+ // L10: JMPIF L03
+ Run(nameof(NeoIssue2723), "VgEC0PsBAGcAAgAAEACIRV8AnUpnACTz");
+ }
+
+ private static void Run(string name, string poc)
+ {
+ byte[] script = Convert.FromBase64String(poc);
+ using ExecutionEngine engine = new();
+ engine.LoadScript(script);
+ Stopwatch stopwatch = Stopwatch.StartNew();
+ engine.Execute();
+ stopwatch.Stop();
+ Debug.Assert(engine.State == VMState.HALT);
+ Console.WriteLine($"Benchmark: {name},\tTime: {stopwatch.Elapsed}");
+ }
+ }
+}
diff --git a/benchmarks/Neo.VM.Benchmarks/Neo.VM.Benchmarks.csproj b/benchmarks/Neo.VM.Benchmarks/Neo.VM.Benchmarks.csproj
new file mode 100644
index 0000000000..bdca77ae89
--- /dev/null
+++ b/benchmarks/Neo.VM.Benchmarks/Neo.VM.Benchmarks.csproj
@@ -0,0 +1,16 @@
+
+
+
+ Exe
+ net7.0
+ Neo.VM
+ enable
+ enable
+ false
+
+
+
+
+
+
+
diff --git a/benchmarks/Neo.VM.Benchmarks/Program.cs b/benchmarks/Neo.VM.Benchmarks/Program.cs
new file mode 100644
index 0000000000..4ab0a66926
--- /dev/null
+++ b/benchmarks/Neo.VM.Benchmarks/Program.cs
@@ -0,0 +1,7 @@
+using Neo.VM;
+using System.Reflection;
+
+foreach (var method in typeof(Benchmarks).GetMethods(BindingFlags.Public | BindingFlags.Static))
+{
+ method.CreateDelegate().Invoke();
+}
diff --git a/neo.sln b/neo.sln
index e705e23988..827bddc1b2 100644
--- a/neo.sln
+++ b/neo.sln
@@ -8,7 +8,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Neo.Json", "src\Neo.Json\Ne
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Neo.UnitTests", "tests\Neo.UnitTests\Neo.UnitTests.csproj", "{5B783B30-B422-4C2F-AC22-187A8D1993F4}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Neo.Json.UnitTests", "tests\Neo.Json.UnitTests\Neo.Json.UnitTests.csproj", "{AE6C32EE-8447-4E01-8187-2AE02BB64251}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Neo.Json.UnitTests", "tests\Neo.Json.UnitTests\Neo.Json.UnitTests.csproj", "{AE6C32EE-8447-4E01-8187-2AE02BB64251}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Neo.Benchmarks", "benchmarks\Neo.Benchmarks\Neo.Benchmarks.csproj", "{BCD03521-5F8F-4775-9ADF-FA361480804F}"
EndProject
@@ -18,6 +18,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{EDE05FA8
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "benchmarks", "benchmarks", "{C25EB0B0-0CAC-4CC1-8F36-F9229EFB99EC}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Neo.VM.Benchmarks", "benchmarks\Neo.VM.Benchmarks\Neo.VM.Benchmarks.csproj", "{E83633BA-FCF0-4A1A-B5BC-42000E24D437}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Neo.VM", "src\Neo.VM\Neo.VM.csproj", "{0603710E-E0BA-494C-AA0F-6FB0C8A8C754}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Neo.VM.Tests", "tests\Neo.VM.Tests\Neo.VM.Tests.csproj", "{005F84EB-EA2E-449F-930A-7B4173DDC7EC}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -44,6 +50,18 @@ Global
{BCD03521-5F8F-4775-9ADF-FA361480804F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BCD03521-5F8F-4775-9ADF-FA361480804F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BCD03521-5F8F-4775-9ADF-FA361480804F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E83633BA-FCF0-4A1A-B5BC-42000E24D437}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E83633BA-FCF0-4A1A-B5BC-42000E24D437}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E83633BA-FCF0-4A1A-B5BC-42000E24D437}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E83633BA-FCF0-4A1A-B5BC-42000E24D437}.Release|Any CPU.Build.0 = Release|Any CPU
+ {0603710E-E0BA-494C-AA0F-6FB0C8A8C754}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {0603710E-E0BA-494C-AA0F-6FB0C8A8C754}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {0603710E-E0BA-494C-AA0F-6FB0C8A8C754}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {0603710E-E0BA-494C-AA0F-6FB0C8A8C754}.Release|Any CPU.Build.0 = Release|Any CPU
+ {005F84EB-EA2E-449F-930A-7B4173DDC7EC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {005F84EB-EA2E-449F-930A-7B4173DDC7EC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {005F84EB-EA2E-449F-930A-7B4173DDC7EC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {005F84EB-EA2E-449F-930A-7B4173DDC7EC}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -54,6 +72,9 @@ Global
{5B783B30-B422-4C2F-AC22-187A8D1993F4} = {EDE05FA8-8E73-4924-BC63-DD117127EEE1}
{AE6C32EE-8447-4E01-8187-2AE02BB64251} = {EDE05FA8-8E73-4924-BC63-DD117127EEE1}
{BCD03521-5F8F-4775-9ADF-FA361480804F} = {C25EB0B0-0CAC-4CC1-8F36-F9229EFB99EC}
+ {E83633BA-FCF0-4A1A-B5BC-42000E24D437} = {C25EB0B0-0CAC-4CC1-8F36-F9229EFB99EC}
+ {0603710E-E0BA-494C-AA0F-6FB0C8A8C754} = {B5339DF7-5D1D-43BA-B332-74B825E1770E}
+ {005F84EB-EA2E-449F-930A-7B4173DDC7EC} = {EDE05FA8-8E73-4924-BC63-DD117127EEE1}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {BCBA19D9-F868-4C6D-8061-A2B91E06E3EC}
diff --git a/src/Neo.VM/BadScriptException.cs b/src/Neo.VM/BadScriptException.cs
new file mode 100644
index 0000000000..fcf4b9f2b4
--- /dev/null
+++ b/src/Neo.VM/BadScriptException.cs
@@ -0,0 +1,31 @@
+// Copyright (C) 2016-2023 The Neo Project.
+//
+// The neo-vm is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project or http://www.opensource.org/licenses/mit-license.php
+// for more details.
+//
+// Redistribution and use in source and binary forms with or without
+// modifications are permitted.
+
+using System;
+
+namespace Neo.VM
+{
+ ///
+ /// Represents the exception thrown when the bad script is parsed.
+ ///
+ public class BadScriptException : Exception
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public BadScriptException() { }
+
+ ///
+ /// Initializes a new instance of the class with a specified error message.
+ ///
+ /// The message that describes the error.
+ public BadScriptException(string message) : base(message) { }
+ }
+}
diff --git a/src/Neo.VM/CatchableException.cs b/src/Neo.VM/CatchableException.cs
new file mode 100644
index 0000000000..9235ac5c31
--- /dev/null
+++ b/src/Neo.VM/CatchableException.cs
@@ -0,0 +1,21 @@
+// Copyright (C) 2016-2023 The Neo Project.
+//
+// The neo-vm is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project or http://www.opensource.org/licenses/mit-license.php
+// for more details.
+//
+// Redistribution and use in source and binary forms with or without
+// modifications are permitted.
+
+using System;
+
+namespace Neo.VM
+{
+ public class CatchableException : Exception
+ {
+ public CatchableException(string message) : base(message)
+ {
+ }
+ }
+}
diff --git a/src/Neo.VM/Collections/OrderedDictionary.cs b/src/Neo.VM/Collections/OrderedDictionary.cs
new file mode 100644
index 0000000000..5f2d593067
--- /dev/null
+++ b/src/Neo.VM/Collections/OrderedDictionary.cs
@@ -0,0 +1,129 @@
+// Copyright (C) 2016-2023 The Neo Project.
+//
+// The neo-vm is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project or http://www.opensource.org/licenses/mit-license.php
+// for more details.
+//
+// Redistribution and use in source and binary forms with or without
+// modifications are permitted.
+
+using System.Collections;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+
+namespace Neo.VM.Collections
+{
+ internal class OrderedDictionary : IDictionary
+ where TKey : notnull
+ {
+ private class TItem
+ {
+ public readonly TKey Key;
+ public TValue Value;
+
+ public TItem(TKey key, TValue value)
+ {
+ this.Key = key;
+ this.Value = value;
+ }
+ }
+
+ private class InternalCollection : KeyedCollection
+ {
+ protected override TKey GetKeyForItem(TItem item)
+ {
+ return item.Key;
+ }
+ }
+
+ private readonly InternalCollection collection = new();
+
+ public int Count => collection.Count;
+ public bool IsReadOnly => false;
+ public ICollection Keys => collection.Select(p => p.Key).ToArray();
+ public ICollection Values => collection.Select(p => p.Value).ToArray();
+
+ public TValue this[TKey key]
+ {
+ get
+ {
+ return collection[key].Value;
+ }
+ set
+ {
+ if (collection.TryGetValue(key, out var entry))
+ entry.Value = value;
+ else
+ Add(key, value);
+ }
+ }
+
+ public void Add(TKey key, TValue value)
+ {
+ collection.Add(new TItem(key, value));
+ }
+
+ public bool ContainsKey(TKey key)
+ {
+ return collection.Contains(key);
+ }
+
+ public bool Remove(TKey key)
+ {
+ return collection.Remove(key);
+ }
+
+ // supress warning of value parameter nullability mismatch
+#pragma warning disable CS8767
+ public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value)
+#pragma warning restore CS8767
+ {
+ if (collection.TryGetValue(key, out var entry))
+ {
+ value = entry.Value;
+ return true;
+ }
+ value = default;
+ return false;
+ }
+
+ void ICollection>.Add(KeyValuePair item)
+ {
+ Add(item.Key, item.Value);
+ }
+
+ public void Clear()
+ {
+ collection.Clear();
+ }
+
+ bool ICollection>.Contains(KeyValuePair item)
+ {
+ return collection.Contains(item.Key);
+ }
+
+ void ICollection>.CopyTo(KeyValuePair[] array, int arrayIndex)
+ {
+ for (int i = 0; i < collection.Count; i++)
+ array[i + arrayIndex] = new KeyValuePair(collection[i].Key, collection[i].Value);
+ }
+
+ bool ICollection>.Remove(KeyValuePair item)
+ {
+ return collection.Remove(item.Key);
+ }
+
+ IEnumerator> IEnumerable>.GetEnumerator()
+ {
+ return collection.Select(p => new KeyValuePair(p.Key, p.Value)).GetEnumerator();
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return collection.Select(p => new KeyValuePair(p.Key, p.Value)).GetEnumerator();
+ }
+ }
+}
diff --git a/src/Neo.VM/Cryptography/BitOperations.cs b/src/Neo.VM/Cryptography/BitOperations.cs
new file mode 100644
index 0000000000..40fc0171af
--- /dev/null
+++ b/src/Neo.VM/Cryptography/BitOperations.cs
@@ -0,0 +1,27 @@
+// Copyright (C) 2016-2023 The Neo Project.
+//
+// The neo-vm is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project or http://www.opensource.org/licenses/mit-license.php
+// for more details.
+//
+// Redistribution and use in source and binary forms with or without
+// modifications are permitted.
+
+using System.Runtime.CompilerServices;
+
+namespace Neo.VM.Cryptography
+{
+#if !NET5_0_OR_GREATER
+ static class BitOperations
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static uint RotateLeft(uint value, int offset)
+ => (value << offset) | (value >> (32 - offset));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ulong RotateLeft(ulong value, int offset)
+ => (value << offset) | (value >> (64 - offset));
+ }
+#endif
+}
diff --git a/src/Neo.VM/Cryptography/Murmur32.cs b/src/Neo.VM/Cryptography/Murmur32.cs
new file mode 100644
index 0000000000..5cfa22d9b7
--- /dev/null
+++ b/src/Neo.VM/Cryptography/Murmur32.cs
@@ -0,0 +1,97 @@
+// Copyright (C) 2016-2023 The Neo Project.
+//
+// The neo-vm is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project or http://www.opensource.org/licenses/mit-license.php
+// for more details.
+//
+// Redistribution and use in source and binary forms with or without
+// modifications are permitted.
+
+using System;
+using System.Buffers.Binary;
+using System.Numerics;
+using System.Security.Cryptography;
+
+namespace Neo.VM.Cryptography
+{
+ ///
+ /// Computes the murmur hash for the input data.
+ ///
+ sealed class Murmur32 : HashAlgorithm
+ {
+ private const uint c1 = 0xcc9e2d51;
+ private const uint c2 = 0x1b873593;
+ private const int r1 = 15;
+ private const int r2 = 13;
+ private const uint m = 5;
+ private const uint n = 0xe6546b64;
+
+ private readonly uint seed;
+ private uint hash;
+ private int length;
+
+ public override int HashSize => 32;
+
+ ///
+ /// Initializes a new instance of the class with the specified seed.
+ ///
+ /// The seed to be used.
+ public Murmur32(uint seed)
+ {
+ this.seed = seed;
+ Initialize();
+ }
+
+ protected override void HashCore(byte[] array, int ibStart, int cbSize)
+ {
+ length += cbSize;
+ int remainder = cbSize & 3;
+ int alignedLength = ibStart + (cbSize - remainder);
+ for (int i = ibStart; i < alignedLength; i += 4)
+ {
+ uint k = BinaryPrimitives.ReadUInt32LittleEndian(array.AsSpan(i));
+ k *= c1;
+ k = BitOperations.RotateLeft(k, r1);
+ k *= c2;
+ hash ^= k;
+ hash = BitOperations.RotateLeft(hash, r2);
+ hash = hash * m + n;
+ }
+ if (remainder > 0)
+ {
+ uint remainingBytes = 0;
+ switch (remainder)
+ {
+ case 3: remainingBytes ^= (uint)array[alignedLength + 2] << 16; goto case 2;
+ case 2: remainingBytes ^= (uint)array[alignedLength + 1] << 8; goto case 1;
+ case 1: remainingBytes ^= array[alignedLength]; break;
+ }
+ remainingBytes *= c1;
+ remainingBytes = BitOperations.RotateLeft(remainingBytes, r1);
+ remainingBytes *= c2;
+ hash ^= remainingBytes;
+ }
+ }
+
+ protected override byte[] HashFinal()
+ {
+ hash ^= (uint)length;
+ hash ^= hash >> 16;
+ hash *= 0x85ebca6b;
+ hash ^= hash >> 13;
+ hash *= 0xc2b2ae35;
+ hash ^= hash >> 16;
+
+ byte[] buffer = new byte[sizeof(uint)];
+ BinaryPrimitives.WriteUInt32LittleEndian(buffer, hash);
+ return buffer;
+ }
+
+ public override void Initialize()
+ {
+ hash = seed;
+ length = 0;
+ }
+ }
+}
diff --git a/src/Neo.VM/Debugger.cs b/src/Neo.VM/Debugger.cs
new file mode 100644
index 0000000000..fef4d87e75
--- /dev/null
+++ b/src/Neo.VM/Debugger.cs
@@ -0,0 +1,137 @@
+// Copyright (C) 2016-2023 The Neo Project.
+//
+// The neo-vm is free software distributed under the MIT software license,
+// see the accompanying file LICENSE in the main directory of the
+// project or http://www.opensource.org/licenses/mit-license.php
+// for more details.
+//
+// Redistribution and use in source and binary forms with or without
+// modifications are permitted.
+
+using System.Collections.Generic;
+
+namespace Neo.VM
+{
+ ///
+ /// A simple debugger for .
+ ///
+ public class Debugger
+ {
+ private readonly ExecutionEngine engine;
+ private readonly Dictionary