From 5e05c82426e3746b9025401a5c56a48a3e5bb073 Mon Sep 17 00:00:00 2001 From: Fernando Diaz Toledano Date: Tue, 6 Feb 2024 19:06:56 +0100 Subject: [PATCH 1/7] Jump table --- src/Neo.VM/ExecutionEngine.cs | 1268 +-------------------------- src/Neo.VM/JumpTable.Control.cs | 180 ++++ src/Neo.VM/JumpTable.Push.cs | 98 +++ src/Neo.VM/JumpTable.cs | 406 +++++++++ src/Neo.VM/OpcodeMethodAttribute.cs | 36 + 5 files changed, 728 insertions(+), 1260 deletions(-) create mode 100644 src/Neo.VM/JumpTable.Control.cs create mode 100644 src/Neo.VM/JumpTable.Push.cs create mode 100644 src/Neo.VM/JumpTable.cs create mode 100644 src/Neo.VM/OpcodeMethodAttribute.cs diff --git a/src/Neo.VM/ExecutionEngine.cs b/src/Neo.VM/ExecutionEngine.cs index f67cc9caec..f020aa24e5 100644 --- a/src/Neo.VM/ExecutionEngine.cs +++ b/src/Neo.VM/ExecutionEngine.cs @@ -12,21 +12,18 @@ using Neo.VM.Types; using System; using System.Collections.Generic; -using System.Linq; -using System.Numerics; using System.Runtime.CompilerServices; -using Buffer = Neo.VM.Types.Buffer; -using VMArray = Neo.VM.Types.Array; namespace Neo.VM { /// /// Represents the VM used to execute the script. /// - public class ExecutionEngine : IDisposable + public partial class ExecutionEngine : IDisposable { private VMState state = VMState.BREAK; private bool isJumping = false; + private readonly JumpTable JumpTable; /// /// Restrictions on the VM. @@ -85,17 +82,19 @@ protected internal set /// /// Initializes a new instance of the class. /// - public ExecutionEngine() : this(new ReferenceCounter(), ExecutionEngineLimits.Default) + public ExecutionEngine(JumpTable? jumpTable = null) : this(jumpTable, new ReferenceCounter(), ExecutionEngineLimits.Default) { } /// /// Initializes a new instance of the class with the specified and . /// + /// The jump table to be used. /// The reference counter to be used. /// Restrictions on the VM. - protected ExecutionEngine(ReferenceCounter referenceCounter, ExecutionEngineLimits limits) + protected ExecutionEngine(JumpTable? jumpTable, ReferenceCounter referenceCounter, ExecutionEngineLimits limits) { + this.JumpTable = jumpTable ?? new JumpTable(); this.Limits = limits; this.ReferenceCounter = referenceCounter; this.ResultStack = new EvaluationStack(referenceCounter); @@ -148,1257 +147,6 @@ private void ExecuteCall(int position) LoadContext(CurrentContext!.Clone(position)); } - private void ExecuteInstruction(Instruction instruction) - { - switch (instruction.OpCode) - { - //Push - case OpCode.PUSHINT8: - case OpCode.PUSHINT16: - case OpCode.PUSHINT32: - case OpCode.PUSHINT64: - case OpCode.PUSHINT128: - case OpCode.PUSHINT256: - { - Push(new BigInteger(instruction.Operand.Span)); - break; - } - case OpCode.PUSHT: - { - Push(StackItem.True); - break; - } - case OpCode.PUSHF: - { - Push(StackItem.False); - break; - } - case OpCode.PUSHA: - { - int position = checked(CurrentContext!.InstructionPointer + instruction.TokenI32); - if (position < 0 || position > CurrentContext.Script.Length) - throw new InvalidOperationException($"Bad pointer address: {position}"); - Push(new Pointer(CurrentContext.Script, position)); - break; - } - case OpCode.PUSHNULL: - { - Push(StackItem.Null); - break; - } - case OpCode.PUSHDATA1: - case OpCode.PUSHDATA2: - case OpCode.PUSHDATA4: - { - Limits.AssertMaxItemSize(instruction.Operand.Length); - Push(instruction.Operand); - break; - } - case OpCode.PUSHM1: - case OpCode.PUSH0: - case OpCode.PUSH1: - case OpCode.PUSH2: - case OpCode.PUSH3: - case OpCode.PUSH4: - case OpCode.PUSH5: - case OpCode.PUSH6: - case OpCode.PUSH7: - case OpCode.PUSH8: - case OpCode.PUSH9: - case OpCode.PUSH10: - case OpCode.PUSH11: - case OpCode.PUSH12: - case OpCode.PUSH13: - case OpCode.PUSH14: - case OpCode.PUSH15: - case OpCode.PUSH16: - { - Push((int)instruction.OpCode - (int)OpCode.PUSH0); - break; - } - - // Control - case OpCode.NOP: break; - case OpCode.JMP: - { - ExecuteJumpOffset(instruction.TokenI8); - break; - } - case OpCode.JMP_L: - { - ExecuteJumpOffset(instruction.TokenI32); - break; - } - case OpCode.JMPIF: - { - if (Pop().GetBoolean()) - ExecuteJumpOffset(instruction.TokenI8); - break; - } - case OpCode.JMPIF_L: - { - if (Pop().GetBoolean()) - ExecuteJumpOffset(instruction.TokenI32); - break; - } - case OpCode.JMPIFNOT: - { - if (!Pop().GetBoolean()) - ExecuteJumpOffset(instruction.TokenI8); - break; - } - case OpCode.JMPIFNOT_L: - { - if (!Pop().GetBoolean()) - ExecuteJumpOffset(instruction.TokenI32); - break; - } - case OpCode.JMPEQ: - { - var x2 = Pop().GetInteger(); - var x1 = Pop().GetInteger(); - if (x1 == x2) - ExecuteJumpOffset(instruction.TokenI8); - break; - } - case OpCode.JMPEQ_L: - { - var x2 = Pop().GetInteger(); - var x1 = Pop().GetInteger(); - if (x1 == x2) - ExecuteJumpOffset(instruction.TokenI32); - break; - } - case OpCode.JMPNE: - { - var x2 = Pop().GetInteger(); - var x1 = Pop().GetInteger(); - if (x1 != x2) - ExecuteJumpOffset(instruction.TokenI8); - break; - } - case OpCode.JMPNE_L: - { - var x2 = Pop().GetInteger(); - var x1 = Pop().GetInteger(); - if (x1 != x2) - ExecuteJumpOffset(instruction.TokenI32); - break; - } - case OpCode.JMPGT: - { - var x2 = Pop().GetInteger(); - var x1 = Pop().GetInteger(); - if (x1 > x2) - ExecuteJumpOffset(instruction.TokenI8); - break; - } - case OpCode.JMPGT_L: - { - var x2 = Pop().GetInteger(); - var x1 = Pop().GetInteger(); - if (x1 > x2) - ExecuteJumpOffset(instruction.TokenI32); - break; - } - case OpCode.JMPGE: - { - var x2 = Pop().GetInteger(); - var x1 = Pop().GetInteger(); - if (x1 >= x2) - ExecuteJumpOffset(instruction.TokenI8); - break; - } - case OpCode.JMPGE_L: - { - var x2 = Pop().GetInteger(); - var x1 = Pop().GetInteger(); - if (x1 >= x2) - ExecuteJumpOffset(instruction.TokenI32); - break; - } - case OpCode.JMPLT: - { - var x2 = Pop().GetInteger(); - var x1 = Pop().GetInteger(); - if (x1 < x2) - ExecuteJumpOffset(instruction.TokenI8); - break; - } - case OpCode.JMPLT_L: - { - var x2 = Pop().GetInteger(); - var x1 = Pop().GetInteger(); - if (x1 < x2) - ExecuteJumpOffset(instruction.TokenI32); - break; - } - case OpCode.JMPLE: - { - var x2 = Pop().GetInteger(); - var x1 = Pop().GetInteger(); - if (x1 <= x2) - ExecuteJumpOffset(instruction.TokenI8); - break; - } - case OpCode.JMPLE_L: - { - var x2 = Pop().GetInteger(); - var x1 = Pop().GetInteger(); - if (x1 <= x2) - ExecuteJumpOffset(instruction.TokenI32); - break; - } - case OpCode.CALL: - { - ExecuteCall(checked(CurrentContext!.InstructionPointer + instruction.TokenI8)); - break; - } - case OpCode.CALL_L: - { - ExecuteCall(checked(CurrentContext!.InstructionPointer + instruction.TokenI32)); - break; - } - case OpCode.CALLA: - { - var x = Pop(); - if (x.Script != CurrentContext!.Script) - throw new InvalidOperationException("Pointers can't be shared between scripts"); - ExecuteCall(x.Position); - break; - } - case OpCode.CALLT: - { - LoadToken(instruction.TokenU16); - break; - } - case OpCode.ABORT: - { - throw new Exception($"{OpCode.ABORT} is executed."); - } - case OpCode.ASSERT: - { - var x = Pop().GetBoolean(); - if (!x) - throw new Exception($"{OpCode.ASSERT} is executed with false result."); - break; - } - case OpCode.THROW: - { - ExecuteThrow(Pop()); - break; - } - case OpCode.TRY: - { - int catchOffset = instruction.TokenI8; - int finallyOffset = instruction.TokenI8_1; - ExecuteTry(catchOffset, finallyOffset); - break; - } - case OpCode.TRY_L: - { - int catchOffset = instruction.TokenI32; - int finallyOffset = instruction.TokenI32_1; - ExecuteTry(catchOffset, finallyOffset); - break; - } - case OpCode.ENDTRY: - { - int endOffset = instruction.TokenI8; - ExecuteEndTry(endOffset); - break; - } - case OpCode.ENDTRY_L: - { - int endOffset = instruction.TokenI32; - ExecuteEndTry(endOffset); - break; - } - case OpCode.ENDFINALLY: - { - if (CurrentContext!.TryStack is null) - throw new InvalidOperationException($"The corresponding TRY block cannot be found."); - if (!CurrentContext.TryStack.TryPop(out ExceptionHandlingContext? currentTry)) - throw new InvalidOperationException($"The corresponding TRY block cannot be found."); - - if (UncaughtException is null) - CurrentContext.InstructionPointer = currentTry.EndPointer; - else - HandleException(); - - isJumping = true; - break; - } - case OpCode.RET: - { - ExecutionContext context_pop = InvocationStack.Pop(); - EvaluationStack stack_eval = InvocationStack.Count == 0 ? ResultStack : InvocationStack.Peek().EvaluationStack; - if (context_pop.EvaluationStack != stack_eval) - { - if (context_pop.RVCount >= 0 && context_pop.EvaluationStack.Count != context_pop.RVCount) - throw new InvalidOperationException("RVCount doesn't match with EvaluationStack"); - context_pop.EvaluationStack.CopyTo(stack_eval); - } - if (InvocationStack.Count == 0) - State = VMState.HALT; - ContextUnloaded(context_pop); - isJumping = true; - break; - } - case OpCode.SYSCALL: - { - OnSysCall(instruction.TokenU32); - break; - } - - // Stack ops - case OpCode.DEPTH: - { - Push(CurrentContext!.EvaluationStack.Count); - break; - } - case OpCode.DROP: - { - Pop(); - break; - } - case OpCode.NIP: - { - CurrentContext!.EvaluationStack.Remove(1); - break; - } - case OpCode.XDROP: - { - int n = (int)Pop().GetInteger(); - if (n < 0) - throw new InvalidOperationException($"The negative value {n} is invalid for OpCode.{instruction.OpCode}."); - CurrentContext!.EvaluationStack.Remove(n); - break; - } - case OpCode.CLEAR: - { - CurrentContext!.EvaluationStack.Clear(); - break; - } - case OpCode.DUP: - { - Push(Peek()); - break; - } - case OpCode.OVER: - { - Push(Peek(1)); - break; - } - case OpCode.PICK: - { - int n = (int)Pop().GetInteger(); - if (n < 0) - throw new InvalidOperationException($"The negative value {n} is invalid for OpCode.{instruction.OpCode}."); - Push(Peek(n)); - break; - } - case OpCode.TUCK: - { - CurrentContext!.EvaluationStack.Insert(2, Peek()); - break; - } - case OpCode.SWAP: - { - var x = CurrentContext!.EvaluationStack.Remove(1); - Push(x); - break; - } - case OpCode.ROT: - { - var x = CurrentContext!.EvaluationStack.Remove(2); - Push(x); - break; - } - case OpCode.ROLL: - { - int n = (int)Pop().GetInteger(); - if (n < 0) - throw new InvalidOperationException($"The negative value {n} is invalid for OpCode.{instruction.OpCode}."); - if (n == 0) break; - var x = CurrentContext!.EvaluationStack.Remove(n); - Push(x); - break; - } - case OpCode.REVERSE3: - { - CurrentContext!.EvaluationStack.Reverse(3); - break; - } - case OpCode.REVERSE4: - { - CurrentContext!.EvaluationStack.Reverse(4); - break; - } - case OpCode.REVERSEN: - { - int n = (int)Pop().GetInteger(); - CurrentContext!.EvaluationStack.Reverse(n); - break; - } - - //Slot - case OpCode.INITSSLOT: - { - if (CurrentContext!.StaticFields != null) - throw new InvalidOperationException($"{instruction.OpCode} cannot be executed twice."); - if (instruction.TokenU8 == 0) - throw new InvalidOperationException($"The operand {instruction.TokenU8} is invalid for OpCode.{instruction.OpCode}."); - CurrentContext.StaticFields = new Slot(instruction.TokenU8, ReferenceCounter); - break; - } - case OpCode.INITSLOT: - { - if (CurrentContext!.LocalVariables != null || CurrentContext.Arguments != null) - throw new InvalidOperationException($"{instruction.OpCode} cannot be executed twice."); - if (instruction.TokenU16 == 0) - throw new InvalidOperationException($"The operand {instruction.TokenU16} is invalid for OpCode.{instruction.OpCode}."); - if (instruction.TokenU8 > 0) - { - CurrentContext.LocalVariables = new Slot(instruction.TokenU8, ReferenceCounter); - } - if (instruction.TokenU8_1 > 0) - { - StackItem[] items = new StackItem[instruction.TokenU8_1]; - for (int i = 0; i < instruction.TokenU8_1; i++) - { - items[i] = Pop(); - } - CurrentContext.Arguments = new Slot(items, ReferenceCounter); - } - break; - } - case OpCode.LDSFLD0: - case OpCode.LDSFLD1: - case OpCode.LDSFLD2: - case OpCode.LDSFLD3: - case OpCode.LDSFLD4: - case OpCode.LDSFLD5: - case OpCode.LDSFLD6: - { - ExecuteLoadFromSlot(CurrentContext!.StaticFields, instruction.OpCode - OpCode.LDSFLD0); - break; - } - case OpCode.LDSFLD: - { - ExecuteLoadFromSlot(CurrentContext!.StaticFields, instruction.TokenU8); - break; - } - case OpCode.STSFLD0: - case OpCode.STSFLD1: - case OpCode.STSFLD2: - case OpCode.STSFLD3: - case OpCode.STSFLD4: - case OpCode.STSFLD5: - case OpCode.STSFLD6: - { - ExecuteStoreToSlot(CurrentContext!.StaticFields, instruction.OpCode - OpCode.STSFLD0); - break; - } - case OpCode.STSFLD: - { - ExecuteStoreToSlot(CurrentContext!.StaticFields, instruction.TokenU8); - break; - } - case OpCode.LDLOC0: - case OpCode.LDLOC1: - case OpCode.LDLOC2: - case OpCode.LDLOC3: - case OpCode.LDLOC4: - case OpCode.LDLOC5: - case OpCode.LDLOC6: - { - ExecuteLoadFromSlot(CurrentContext!.LocalVariables, instruction.OpCode - OpCode.LDLOC0); - break; - } - case OpCode.LDLOC: - { - ExecuteLoadFromSlot(CurrentContext!.LocalVariables, instruction.TokenU8); - break; - } - case OpCode.STLOC0: - case OpCode.STLOC1: - case OpCode.STLOC2: - case OpCode.STLOC3: - case OpCode.STLOC4: - case OpCode.STLOC5: - case OpCode.STLOC6: - { - ExecuteStoreToSlot(CurrentContext!.LocalVariables, instruction.OpCode - OpCode.STLOC0); - break; - } - case OpCode.STLOC: - { - ExecuteStoreToSlot(CurrentContext!.LocalVariables, instruction.TokenU8); - break; - } - case OpCode.LDARG0: - case OpCode.LDARG1: - case OpCode.LDARG2: - case OpCode.LDARG3: - case OpCode.LDARG4: - case OpCode.LDARG5: - case OpCode.LDARG6: - { - ExecuteLoadFromSlot(CurrentContext!.Arguments, instruction.OpCode - OpCode.LDARG0); - break; - } - case OpCode.LDARG: - { - ExecuteLoadFromSlot(CurrentContext!.Arguments, instruction.TokenU8); - break; - } - case OpCode.STARG0: - case OpCode.STARG1: - case OpCode.STARG2: - case OpCode.STARG3: - case OpCode.STARG4: - case OpCode.STARG5: - case OpCode.STARG6: - { - ExecuteStoreToSlot(CurrentContext!.Arguments, instruction.OpCode - OpCode.STARG0); - break; - } - case OpCode.STARG: - { - ExecuteStoreToSlot(CurrentContext!.Arguments, instruction.TokenU8); - break; - } - - // Splice - case OpCode.NEWBUFFER: - { - int length = (int)Pop().GetInteger(); - Limits.AssertMaxItemSize(length); - Push(new Buffer(length)); - break; - } - case OpCode.MEMCPY: - { - int count = (int)Pop().GetInteger(); - if (count < 0) - throw new InvalidOperationException($"The value {count} is out of range."); - int si = (int)Pop().GetInteger(); - if (si < 0) - throw new InvalidOperationException($"The value {si} is out of range."); - ReadOnlySpan src = Pop().GetSpan(); - if (checked(si + count) > src.Length) - throw new InvalidOperationException($"The value {count} is out of range."); - int di = (int)Pop().GetInteger(); - if (di < 0) - throw new InvalidOperationException($"The value {di} is out of range."); - Buffer dst = Pop(); - if (checked(di + count) > dst.Size) - throw new InvalidOperationException($"The value {count} is out of range."); - src.Slice(si, count).CopyTo(dst.InnerBuffer.Span[di..]); - break; - } - case OpCode.CAT: - { - var x2 = Pop().GetSpan(); - var x1 = Pop().GetSpan(); - int length = x1.Length + x2.Length; - Limits.AssertMaxItemSize(length); - Buffer result = new(length, false); - x1.CopyTo(result.InnerBuffer.Span); - x2.CopyTo(result.InnerBuffer.Span[x1.Length..]); - Push(result); - break; - } - case OpCode.SUBSTR: - { - int count = (int)Pop().GetInteger(); - if (count < 0) - throw new InvalidOperationException($"The value {count} is out of range."); - int index = (int)Pop().GetInteger(); - if (index < 0) - throw new InvalidOperationException($"The value {index} is out of range."); - var x = Pop().GetSpan(); - if (index + count > x.Length) - throw new InvalidOperationException($"The value {count} is out of range."); - Buffer result = new(count, false); - x.Slice(index, count).CopyTo(result.InnerBuffer.Span); - Push(result); - break; - } - case OpCode.LEFT: - { - int count = (int)Pop().GetInteger(); - if (count < 0) - throw new InvalidOperationException($"The value {count} is out of range."); - var x = Pop().GetSpan(); - if (count > x.Length) - throw new InvalidOperationException($"The value {count} is out of range."); - Buffer result = new(count, false); - x[..count].CopyTo(result.InnerBuffer.Span); - Push(result); - break; - } - case OpCode.RIGHT: - { - int count = (int)Pop().GetInteger(); - if (count < 0) - throw new InvalidOperationException($"The value {count} is out of range."); - var x = Pop().GetSpan(); - if (count > x.Length) - throw new InvalidOperationException($"The value {count} is out of range."); - Buffer result = new(count, false); - x[^count..^0].CopyTo(result.InnerBuffer.Span); - Push(result); - break; - } - - // Bitwise logic - case OpCode.INVERT: - { - var x = Pop().GetInteger(); - Push(~x); - break; - } - case OpCode.AND: - { - var x2 = Pop().GetInteger(); - var x1 = Pop().GetInteger(); - Push(x1 & x2); - break; - } - case OpCode.OR: - { - var x2 = Pop().GetInteger(); - var x1 = Pop().GetInteger(); - Push(x1 | x2); - break; - } - case OpCode.XOR: - { - var x2 = Pop().GetInteger(); - var x1 = Pop().GetInteger(); - Push(x1 ^ x2); - break; - } - case OpCode.EQUAL: - { - StackItem x2 = Pop(); - StackItem x1 = Pop(); - Push(x1.Equals(x2, Limits)); - break; - } - case OpCode.NOTEQUAL: - { - StackItem x2 = Pop(); - StackItem x1 = Pop(); - Push(!x1.Equals(x2, Limits)); - break; - } - - // Numeric - case OpCode.SIGN: - { - var x = Pop().GetInteger(); - Push(x.Sign); - break; - } - case OpCode.ABS: - { - var x = Pop().GetInteger(); - Push(BigInteger.Abs(x)); - break; - } - case OpCode.NEGATE: - { - var x = Pop().GetInteger(); - Push(-x); - break; - } - case OpCode.INC: - { - var x = Pop().GetInteger(); - Push(x + 1); - break; - } - case OpCode.DEC: - { - var x = Pop().GetInteger(); - Push(x - 1); - break; - } - case OpCode.ADD: - { - var x2 = Pop().GetInteger(); - var x1 = Pop().GetInteger(); - Push(x1 + x2); - break; - } - case OpCode.SUB: - { - var x2 = Pop().GetInteger(); - var x1 = Pop().GetInteger(); - Push(x1 - x2); - break; - } - case OpCode.MUL: - { - var x2 = Pop().GetInteger(); - var x1 = Pop().GetInteger(); - Push(x1 * x2); - break; - } - case OpCode.DIV: - { - var x2 = Pop().GetInteger(); - var x1 = Pop().GetInteger(); - Push(x1 / x2); - break; - } - case OpCode.MOD: - { - var x2 = Pop().GetInteger(); - var x1 = Pop().GetInteger(); - Push(x1 % x2); - break; - } - case OpCode.POW: - { - var exponent = (int)Pop().GetInteger(); - Limits.AssertShift(exponent); - var value = Pop().GetInteger(); - Push(BigInteger.Pow(value, exponent)); - break; - } - case OpCode.SQRT: - { - Push(Pop().GetInteger().Sqrt()); - break; - } - case OpCode.MODMUL: - { - var modulus = Pop().GetInteger(); - var x2 = Pop().GetInteger(); - var x1 = Pop().GetInteger(); - Push(x1 * x2 % modulus); - break; - } - case OpCode.MODPOW: - { - var modulus = Pop().GetInteger(); - var exponent = Pop().GetInteger(); - var value = Pop().GetInteger(); - var result = exponent == -1 - ? value.ModInverse(modulus) - : BigInteger.ModPow(value, exponent, modulus); - Push(result); - break; - } - case OpCode.SHL: - { - int shift = (int)Pop().GetInteger(); - Limits.AssertShift(shift); - if (shift == 0) break; - var x = Pop().GetInteger(); - Push(x << shift); - break; - } - case OpCode.SHR: - { - int shift = (int)Pop().GetInteger(); - Limits.AssertShift(shift); - if (shift == 0) break; - var x = Pop().GetInteger(); - Push(x >> shift); - break; - } - case OpCode.NOT: - { - var x = Pop().GetBoolean(); - Push(!x); - break; - } - case OpCode.BOOLAND: - { - var x2 = Pop().GetBoolean(); - var x1 = Pop().GetBoolean(); - Push(x1 && x2); - break; - } - case OpCode.BOOLOR: - { - var x2 = Pop().GetBoolean(); - var x1 = Pop().GetBoolean(); - Push(x1 || x2); - break; - } - case OpCode.NZ: - { - var x = Pop().GetInteger(); - Push(!x.IsZero); - break; - } - case OpCode.NUMEQUAL: - { - var x2 = Pop().GetInteger(); - var x1 = Pop().GetInteger(); - Push(x1 == x2); - break; - } - case OpCode.NUMNOTEQUAL: - { - var x2 = Pop().GetInteger(); - var x1 = Pop().GetInteger(); - Push(x1 != x2); - break; - } - case OpCode.LT: - { - var x2 = Pop(); - var x1 = Pop(); - if (x1.IsNull || x2.IsNull) - Push(false); - else - Push(x1.GetInteger() < x2.GetInteger()); - break; - } - case OpCode.LE: - { - var x2 = Pop(); - var x1 = Pop(); - if (x1.IsNull || x2.IsNull) - Push(false); - else - Push(x1.GetInteger() <= x2.GetInteger()); - break; - } - case OpCode.GT: - { - var x2 = Pop(); - var x1 = Pop(); - if (x1.IsNull || x2.IsNull) - Push(false); - else - Push(x1.GetInteger() > x2.GetInteger()); - break; - } - case OpCode.GE: - { - var x2 = Pop(); - var x1 = Pop(); - if (x1.IsNull || x2.IsNull) - Push(false); - else - Push(x1.GetInteger() >= x2.GetInteger()); - break; - } - case OpCode.MIN: - { - var x2 = Pop().GetInteger(); - var x1 = Pop().GetInteger(); - Push(BigInteger.Min(x1, x2)); - break; - } - case OpCode.MAX: - { - var x2 = Pop().GetInteger(); - var x1 = Pop().GetInteger(); - Push(BigInteger.Max(x1, x2)); - break; - } - case OpCode.WITHIN: - { - BigInteger b = Pop().GetInteger(); - BigInteger a = Pop().GetInteger(); - var x = Pop().GetInteger(); - Push(a <= x && x < b); - break; - } - - // Compound-type - case OpCode.PACKMAP: - { - int size = (int)Pop().GetInteger(); - if (size < 0 || size * 2 > CurrentContext!.EvaluationStack.Count) - throw new InvalidOperationException($"The value {size} is out of range."); - Map map = new(ReferenceCounter); - for (int i = 0; i < size; i++) - { - PrimitiveType key = Pop(); - StackItem value = Pop(); - map[key] = value; - } - Push(map); - break; - } - case OpCode.PACKSTRUCT: - { - int size = (int)Pop().GetInteger(); - if (size < 0 || size > CurrentContext!.EvaluationStack.Count) - throw new InvalidOperationException($"The value {size} is out of range."); - Struct @struct = new(ReferenceCounter); - for (int i = 0; i < size; i++) - { - StackItem item = Pop(); - @struct.Add(item); - } - Push(@struct); - break; - } - case OpCode.PACK: - { - int size = (int)Pop().GetInteger(); - if (size < 0 || size > CurrentContext!.EvaluationStack.Count) - throw new InvalidOperationException($"The value {size} is out of range."); - VMArray array = new(ReferenceCounter); - for (int i = 0; i < size; i++) - { - StackItem item = Pop(); - array.Add(item); - } - Push(array); - break; - } - case OpCode.UNPACK: - { - CompoundType compound = Pop(); - switch (compound) - { - case Map map: - foreach (var (key, value) in map.Reverse()) - { - Push(value); - Push(key); - } - break; - case VMArray array: - for (int i = array.Count - 1; i >= 0; i--) - { - Push(array[i]); - } - break; - default: - throw new InvalidOperationException($"Invalid type for {instruction.OpCode}: {compound.Type}"); - } - Push(compound.Count); - break; - } - case OpCode.NEWARRAY0: - { - Push(new VMArray(ReferenceCounter)); - break; - } - case OpCode.NEWARRAY: - case OpCode.NEWARRAY_T: - { - int n = (int)Pop().GetInteger(); - if (n < 0 || n > Limits.MaxStackSize) - throw new InvalidOperationException($"MaxStackSize exceed: {n}"); - StackItem item; - if (instruction.OpCode == OpCode.NEWARRAY_T) - { - StackItemType type = (StackItemType)instruction.TokenU8; - if (!Enum.IsDefined(typeof(StackItemType), type)) - throw new InvalidOperationException($"Invalid type for {instruction.OpCode}: {instruction.TokenU8}"); - item = instruction.TokenU8 switch - { - (byte)StackItemType.Boolean => StackItem.False, - (byte)StackItemType.Integer => Integer.Zero, - (byte)StackItemType.ByteString => ByteString.Empty, - _ => StackItem.Null - }; - } - else - { - item = StackItem.Null; - } - Push(new VMArray(ReferenceCounter, Enumerable.Repeat(item, n))); - break; - } - case OpCode.NEWSTRUCT0: - { - Push(new Struct(ReferenceCounter)); - break; - } - case OpCode.NEWSTRUCT: - { - int n = (int)Pop().GetInteger(); - if (n < 0 || n > Limits.MaxStackSize) - throw new InvalidOperationException($"MaxStackSize exceed: {n}"); - Struct result = new(ReferenceCounter); - for (var i = 0; i < n; i++) - result.Add(StackItem.Null); - Push(result); - break; - } - case OpCode.NEWMAP: - { - Push(new Map(ReferenceCounter)); - break; - } - case OpCode.SIZE: - { - var x = Pop(); - switch (x) - { - case CompoundType compound: - Push(compound.Count); - break; - case PrimitiveType primitive: - Push(primitive.Size); - break; - case Buffer buffer: - Push(buffer.Size); - break; - default: - throw new InvalidOperationException($"Invalid type for {instruction.OpCode}: {x.Type}"); - } - break; - } - case OpCode.HASKEY: - { - PrimitiveType key = Pop(); - var x = Pop(); - switch (x) - { - case VMArray array: - { - int index = (int)key.GetInteger(); - if (index < 0) - throw new InvalidOperationException($"The negative value {index} is invalid for OpCode.{instruction.OpCode}."); - Push(index < array.Count); - break; - } - case Map map: - { - Push(map.ContainsKey(key)); - break; - } - case Buffer buffer: - { - int index = (int)key.GetInteger(); - if (index < 0) - throw new InvalidOperationException($"The negative value {index} is invalid for OpCode.{instruction.OpCode}."); - Push(index < buffer.Size); - break; - } - case ByteString array: - { - int index = (int)key.GetInteger(); - if (index < 0) - throw new InvalidOperationException($"The negative value {index} is invalid for OpCode.{instruction.OpCode}."); - Push(index < array.Size); - break; - } - default: - throw new InvalidOperationException($"Invalid type for {instruction.OpCode}: {x.Type}"); - } - break; - } - case OpCode.KEYS: - { - Map map = Pop(); - Push(new VMArray(ReferenceCounter, map.Keys)); - break; - } - case OpCode.VALUES: - { - var x = Pop(); - IEnumerable values = x switch - { - VMArray array => array, - Map map => map.Values, - _ => throw new InvalidOperationException($"Invalid type for {instruction.OpCode}: {x.Type}"), - }; - VMArray newArray = new(ReferenceCounter); - foreach (StackItem item in values) - if (item is Struct s) - newArray.Add(s.Clone(Limits)); - else - newArray.Add(item); - Push(newArray); - break; - } - case OpCode.PICKITEM: - { - PrimitiveType key = Pop(); - var x = Pop(); - switch (x) - { - case VMArray array: - { - int index = (int)key.GetInteger(); - if (index < 0 || index >= array.Count) - throw new CatchableException($"The value {index} is out of range."); - Push(array[index]); - break; - } - case Map map: - { - if (!map.TryGetValue(key, out StackItem? value)) - throw new CatchableException($"Key not found in {nameof(Map)}"); - Push(value); - break; - } - case PrimitiveType primitive: - { - ReadOnlySpan byteArray = primitive.GetSpan(); - int index = (int)key.GetInteger(); - if (index < 0 || index >= byteArray.Length) - throw new CatchableException($"The value {index} is out of range."); - Push((BigInteger)byteArray[index]); - break; - } - case Buffer buffer: - { - int index = (int)key.GetInteger(); - if (index < 0 || index >= buffer.Size) - throw new CatchableException($"The value {index} is out of range."); - Push((BigInteger)buffer.InnerBuffer.Span[index]); - break; - } - default: - throw new InvalidOperationException($"Invalid type for {instruction.OpCode}: {x.Type}"); - } - break; - } - case OpCode.APPEND: - { - StackItem newItem = Pop(); - VMArray array = Pop(); - if (newItem is Struct s) newItem = s.Clone(Limits); - array.Add(newItem); - break; - } - case OpCode.SETITEM: - { - StackItem value = Pop(); - if (value is Struct s) value = s.Clone(Limits); - PrimitiveType key = Pop(); - var x = Pop(); - switch (x) - { - case VMArray array: - { - int index = (int)key.GetInteger(); - if (index < 0 || index >= array.Count) - throw new CatchableException($"The value {index} is out of range."); - array[index] = value; - break; - } - case Map map: - { - map[key] = value; - break; - } - case Buffer buffer: - { - int index = (int)key.GetInteger(); - if (index < 0 || index >= buffer.Size) - throw new CatchableException($"The value {index} is out of range."); - if (value is not PrimitiveType p) - throw new InvalidOperationException($"Value must be a primitive type in {instruction.OpCode}"); - int b = (int)p.GetInteger(); - if (b < sbyte.MinValue || b > byte.MaxValue) - throw new InvalidOperationException($"Overflow in {instruction.OpCode}, {b} is not a byte type."); - buffer.InnerBuffer.Span[index] = (byte)b; - break; - } - default: - throw new InvalidOperationException($"Invalid type for {instruction.OpCode}: {x.Type}"); - } - break; - } - case OpCode.REVERSEITEMS: - { - var x = Pop(); - switch (x) - { - case VMArray array: - array.Reverse(); - break; - case Buffer buffer: - buffer.InnerBuffer.Span.Reverse(); - break; - default: - throw new InvalidOperationException($"Invalid type for {instruction.OpCode}: {x.Type}"); - } - break; - } - case OpCode.REMOVE: - { - PrimitiveType key = Pop(); - var x = Pop(); - switch (x) - { - case VMArray array: - int index = (int)key.GetInteger(); - if (index < 0 || index >= array.Count) - throw new InvalidOperationException($"The value {index} is out of range."); - array.RemoveAt(index); - break; - case Map map: - map.Remove(key); - break; - default: - throw new InvalidOperationException($"Invalid type for {instruction.OpCode}: {x.Type}"); - } - break; - } - case OpCode.CLEARITEMS: - { - CompoundType x = Pop(); - x.Clear(); - break; - } - case OpCode.POPITEM: - { - VMArray x = Pop(); - int index = x.Count - 1; - Push(x[index]); - x.RemoveAt(index); - break; - } - - //Types - case OpCode.ISNULL: - { - var x = Pop(); - Push(x.IsNull); - break; - } - case OpCode.ISTYPE: - { - var x = Pop(); - StackItemType type = (StackItemType)instruction.TokenU8; - if (type == StackItemType.Any || !Enum.IsDefined(typeof(StackItemType), type)) - throw new InvalidOperationException($"Invalid type: {type}"); - Push(x.Type == type); - break; - } - case OpCode.CONVERT: - { - var x = Pop(); - Push(x.ConvertTo((StackItemType)instruction.TokenU8)); - break; - } - case OpCode.ABORTMSG: - { - var msg = Pop().GetString(); - throw new Exception($"{OpCode.ABORTMSG} is executed. Reason: {msg}"); - } - case OpCode.ASSERTMSG: - { - var msg = Pop().GetString(); - var x = Pop().GetBoolean(); - if (!x) - throw new Exception($"{OpCode.ASSERTMSG} is executed with false result. Reason: {msg}"); - break; - } - default: throw new InvalidOperationException($"Opcode {instruction.OpCode} is undefined."); - } - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] private void ExecuteEndTry(int endOffset) { @@ -1442,7 +190,7 @@ protected void ExecuteJump(int position) /// /// The offset from the current position to jump to. [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected void ExecuteJumpOffset(int offset) + public void ExecuteJumpOffset(int offset) { ExecuteJump(checked(CurrentContext!.InstructionPointer + offset)); } @@ -1474,7 +222,7 @@ protected internal void ExecuteNext() PreExecuteInstruction(instruction); try { - ExecuteInstruction(instruction); + JumpTable.GetMethod(instruction.OpCode)(this, instruction); } catch (CatchableException ex) when (Limits.CatchEngineExceptions) { diff --git a/src/Neo.VM/JumpTable.Control.cs b/src/Neo.VM/JumpTable.Control.cs new file mode 100644 index 0000000000..0c2a2aa694 --- /dev/null +++ b/src/Neo.VM/JumpTable.Control.cs @@ -0,0 +1,180 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// JumpTable.Control.cs file belongs to the neo project and is free +// software distributed under the MIT software license, see the +// accompanying file LICENSE in the main directory of the +// repository 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 +{ + public partial class JumpTable + { + /* TODO + case OpCode.JMPGT: + JmpGt(instruction); + break; + case OpCode.JMPGT_L: + JmpGt_L(instruction); + break; + case OpCode.JMPGE: + JmpGe(instruction); + break; + case OpCode.JMPGE_L: + JmpGe_L(instruction); + break; + case OpCode.JMPLT: + JmpLt(instruction); + break; + case OpCode.JMPLT_L: + JmpLt_L(instruction); + break; + case OpCode.JMPLE: + JmpLe(instruction); + break; + case OpCode.JMPLE_L: + JmpLe_L(instruction); + break; + case OpCode.CALL: + Call(instruction); + break; + case OpCode.CALL_L: + Call_L(instruction); + break; + case OpCode.CALLA: + CallA(instruction); + break; + case OpCode.CALLT: + CallT(instruction); + break; + case OpCode.ABORT: + Abort(instruction); + break; + case OpCode.ASSERT: + Assert(instruction); + break; + case OpCode.THROW: + Throw(instruction); + break; + case OpCode.TRY: + Try(instruction); + break; + case OpCode.TRY_L: + Try_L(instruction); + break; + case OpCode.ENDTRY: + EndTry(instruction); + break; + case OpCode.ENDTRY_L: + EndTry_L(instruction); + break; + case OpCode.ENDFINALLY: + EndFinally(instruction); + break; + case OpCode.RET: + Ret(instruction); + break; + case OpCode.SYSCALL: + Syscall(instruction); + break; + + */ + + [OpcodeMethod(OpCode.NOP)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Nop(ExecutionEngine engine, Instruction instruction) + { + } + + [OpcodeMethod(OpCode.JMP)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Jmp(ExecutionEngine engine, Instruction instruction) + { + engine.ExecuteJumpOffset(instruction.TokenI8); + } + + [OpcodeMethod(OpCode.JMP_L)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void JmpL(ExecutionEngine engine, Instruction instruction) + { + engine.ExecuteJumpOffset(instruction.TokenI32); + } + + [OpcodeMethod(OpCode.JMPIF)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void JmpIf(ExecutionEngine engine, Instruction instruction) + { + if (engine.Pop().GetBoolean()) + engine.ExecuteJumpOffset(instruction.TokenI8); + } + + [OpcodeMethod(OpCode.JMPIF_L)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void JmpIfL(ExecutionEngine engine, Instruction instruction) + { + if (engine.Pop().GetBoolean()) + engine.ExecuteJumpOffset(instruction.TokenI32); + } + + [OpcodeMethod(OpCode.JMPIFNOT)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void JmpIfNot(ExecutionEngine engine, Instruction instruction) + { + if (!engine.Pop().GetBoolean()) + engine.ExecuteJumpOffset(instruction.TokenI8); + } + + [OpcodeMethod(OpCode.JMPIFNOT_L)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void JmpIfNotL(ExecutionEngine engine, Instruction instruction) + { + if (!engine.Pop().GetBoolean()) + engine.ExecuteJumpOffset(instruction.TokenI32); + } + + [OpcodeMethod(OpCode.JMPEQ)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void JmpEq(ExecutionEngine engine, Instruction instruction) + { + var x2 = engine.Pop().GetInteger(); + var x1 = engine.Pop().GetInteger(); + if (x1 == x2) + engine.ExecuteJumpOffset(instruction.TokenI8); + } + + [OpcodeMethod(OpCode.JMPEQ_L)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void JmpEqL(ExecutionEngine engine, Instruction instruction) + { + var x2 = engine.Pop().GetInteger(); + var x1 = engine.Pop().GetInteger(); + if (x1 == x2) + engine.ExecuteJumpOffset(instruction.TokenI32); + } + + [OpcodeMethod(OpCode.JMPNE)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void JmpNe(ExecutionEngine engine, Instruction instruction) + { + var x2 = engine.Pop().GetInteger(); + var x1 = engine.Pop().GetInteger(); + if (x1 != x2) + engine.ExecuteJumpOffset(instruction.TokenI8); + } + + [OpcodeMethod(OpCode.JMPNE_L)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void JmpNeL(ExecutionEngine engine, Instruction instruction) + { + var x2 = engine.Pop().GetInteger(); + var x1 = engine.Pop().GetInteger(); + if (x1 != x2) + engine.ExecuteJumpOffset(instruction.TokenI32); + } + } +} diff --git a/src/Neo.VM/JumpTable.Push.cs b/src/Neo.VM/JumpTable.Push.cs new file mode 100644 index 0000000000..e15910778a --- /dev/null +++ b/src/Neo.VM/JumpTable.Push.cs @@ -0,0 +1,98 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// JumpTable.Push.cs file belongs to the neo project and is free +// software distributed under the MIT software license, see the +// accompanying file LICENSE in the main directory of the +// repository 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 Neo.VM.Types; +using System; +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace Neo.VM +{ + public partial class JumpTable + { + [OpcodeMethod(OpCode.PUSHINT8)] + [OpcodeMethod(OpCode.PUSHINT16)] + [OpcodeMethod(OpCode.PUSHINT32)] + [OpcodeMethod(OpCode.PUSHINT64)] + [OpcodeMethod(OpCode.PUSHINT128)] + [OpcodeMethod(OpCode.PUSHINT256)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void PushInt(ExecutionEngine engine, Instruction instruction) + { + engine.Push(new BigInteger(instruction.Operand.Span)); + } + + [OpcodeMethod(OpCode.PUSHT)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void PushT(ExecutionEngine engine, Instruction instruction) + { + engine.Push(StackItem.True); + } + + [OpcodeMethod(OpCode.PUSHF)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void PushF(ExecutionEngine engine, Instruction instruction) + { + engine.Push(StackItem.False); + } + + [OpcodeMethod(OpCode.PUSHA)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void PushA(ExecutionEngine engine, Instruction instruction) + { + var position = checked(engine.CurrentContext!.InstructionPointer + instruction.TokenI32); + if (position < 0 || position > engine.CurrentContext.Script.Length) + throw new InvalidOperationException($"Bad pointer address(Instruction instruction) {position}"); + engine.Push(new Pointer(engine.CurrentContext.Script, position)); + } + + [OpcodeMethod(OpCode.PUSHNULL)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void PushNull(ExecutionEngine engine, Instruction instruction) + { + engine.Push(StackItem.Null); + } + + [OpcodeMethod(OpCode.PUSHDATA1)] + [OpcodeMethod(OpCode.PUSHDATA2)] + [OpcodeMethod(OpCode.PUSHDATA4)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void PushData(ExecutionEngine engine, Instruction instruction) + { + engine.Limits.AssertMaxItemSize(instruction.Operand.Length); + engine.Push(instruction.Operand); + } + + [OpcodeMethod(OpCode.PUSHM1)] + [OpcodeMethod(OpCode.PUSH0)] + [OpcodeMethod(OpCode.PUSH1)] + [OpcodeMethod(OpCode.PUSH2)] + [OpcodeMethod(OpCode.PUSH3)] + [OpcodeMethod(OpCode.PUSH4)] + [OpcodeMethod(OpCode.PUSH5)] + [OpcodeMethod(OpCode.PUSH6)] + [OpcodeMethod(OpCode.PUSH7)] + [OpcodeMethod(OpCode.PUSH8)] + [OpcodeMethod(OpCode.PUSH9)] + [OpcodeMethod(OpCode.PUSH10)] + [OpcodeMethod(OpCode.PUSH11)] + [OpcodeMethod(OpCode.PUSH12)] + [OpcodeMethod(OpCode.PUSH13)] + [OpcodeMethod(OpCode.PUSH14)] + [OpcodeMethod(OpCode.PUSH15)] + [OpcodeMethod(OpCode.PUSH16)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Push(ExecutionEngine engine, Instruction instruction) + { + engine.Push((int)instruction.OpCode - (int)OpCode.PUSH0); + } + } +} diff --git a/src/Neo.VM/JumpTable.cs b/src/Neo.VM/JumpTable.cs new file mode 100644 index 0000000000..0a352567a0 --- /dev/null +++ b/src/Neo.VM/JumpTable.cs @@ -0,0 +1,406 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// JumpTable.cs file belongs to the neo project and is free +// software distributed under the MIT software license, see the +// accompanying file LICENSE in the main directory of the +// repository 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.Reflection; + +namespace Neo.VM +{ + public partial class JumpTable + { + public delegate void delAction(ExecutionEngine engine, Instruction instruction); + public readonly delAction[] Table = new delAction[byte.MaxValue]; + + /// + /// Get Method + /// + /// OpCode + /// Action + public delAction GetMethod(OpCode opCode) + { + return Table[(byte)opCode]; + } + + public JumpTable() + { + // Fill defined + + foreach (var mi in GetType().GetMethods()) + { + foreach (var attr in mi.GetCustomAttributes(true)) + { + if (Table[(byte)attr.OpCode] is not null) + { + throw new InvalidOperationException($"Opcode {attr.OpCode} is already defined."); + } + + Table[(byte)attr.OpCode] = (delAction)mi.CreateDelegate(typeof(delAction), this); + } + } + + // Fill with undefined + + for (int x = 0; x < Table.Length; x++) + { + if (Table[x] is not null) continue; + + Table[x] = (engine, instruction) => + { + throw new InvalidOperationException($"Opcode {instruction.OpCode} is undefined."); + }; + } + + /* + switch (instruction.OpCode) + { + // Stack ops + case OpCode.DEPTH: + Depth(instruction); + break; + case OpCode.DROP: + Drop(instruction); + break; + case OpCode.NIP: + Nip(instruction); + break; + case OpCode.XDROP: + XDrop(instruction); + break; + case OpCode.CLEAR: + Clear(instruction); + break; + case OpCode.DUP: + Dup(instruction); + break; + case OpCode.OVER: + Over(instruction); + break; + case OpCode.PICK: + Pick(instruction); + break; + case OpCode.TUCK: + Tuck(instruction); + break; + case OpCode.SWAP: + Swap(instruction); + break; + case OpCode.ROT: + Rot(instruction); + break; + case OpCode.ROLL: + Roll(instruction); + break; + case OpCode.REVERSE3: + Reverse3(instruction); + break; + case OpCode.REVERSE4: + Reverse4(instruction); + break; + case OpCode.REVERSEN: + ReverseN(instruction); + break; + + //Slot + case OpCode.INITSSLOT: + InitSSlot(instruction); + break; + case OpCode.INITSLOT: + InitSlot(instruction); + break; + case OpCode.LDSFLD0: + case OpCode.LDSFLD1: + case OpCode.LDSFLD2: + case OpCode.LDSFLD3: + case OpCode.LDSFLD4: + case OpCode.LDSFLD5: + case OpCode.LDSFLD6: + LdSFldM(instruction); + break; + case OpCode.LDSFLD: + LdSFld(instruction); + break; + case OpCode.STSFLD0: + case OpCode.STSFLD1: + case OpCode.STSFLD2: + case OpCode.STSFLD3: + case OpCode.STSFLD4: + case OpCode.STSFLD5: + case OpCode.STSFLD6: + StSFldM(instruction); + break; + case OpCode.STSFLD: + StSFld(instruction); + break; + case OpCode.LDLOC0: + case OpCode.LDLOC1: + case OpCode.LDLOC2: + case OpCode.LDLOC3: + case OpCode.LDLOC4: + case OpCode.LDLOC5: + case OpCode.LDLOC6: + LdLocM(instruction); + break; + case OpCode.LDLOC: + LdLoc(instruction); + break; + case OpCode.STLOC0: + case OpCode.STLOC1: + case OpCode.STLOC2: + case OpCode.STLOC3: + case OpCode.STLOC4: + case OpCode.STLOC5: + case OpCode.STLOC6: + StLocM(instruction); + break; + case OpCode.STLOC: + StLoc(instruction); + break; + case OpCode.LDARG0: + case OpCode.LDARG1: + case OpCode.LDARG2: + case OpCode.LDARG3: + case OpCode.LDARG4: + case OpCode.LDARG5: + case OpCode.LDARG6: + LdArgM(instruction); + break; + case OpCode.LDARG: + LdArg(instruction); + break; + case OpCode.STARG0: + case OpCode.STARG1: + case OpCode.STARG2: + case OpCode.STARG3: + case OpCode.STARG4: + case OpCode.STARG5: + case OpCode.STARG6: + StArgM(instruction); + break; + case OpCode.STARG: + StArg(instruction); + break; + + // Splice + case OpCode.NEWBUFFER: + NewBuffer(instruction); + break; + case OpCode.MEMCPY: + Memcpy(instruction); + break; + case OpCode.CAT: + Cat(instruction); + break; + case OpCode.SUBSTR: + Substr(instruction); + break; + case OpCode.LEFT: + Left(instruction); + break; + case OpCode.RIGHT: + Right(instruction); + break; + + // Bitwise logic + case OpCode.INVERT: + Invert(instruction); + break; + case OpCode.AND: + And(instruction); + break; + case OpCode.OR: + Or(instruction); + break; + case OpCode.XOR: + Xor(instruction); + break; + case OpCode.EQUAL: + Equal(instruction); + break; + case OpCode.NOTEQUAL: + NotEqual(instruction); + break; + + // Numeric + case OpCode.SIGN: + Sign(instruction); + break; + case OpCode.ABS: + Abs(instruction); + break; + case OpCode.NEGATE: + Negate(instruction); + break; + case OpCode.INC: + Inc(instruction); + break; + case OpCode.DEC: + Dec(instruction); + break; + case OpCode.ADD: + Add(instruction); + break; + case OpCode.SUB: + Sub(instruction); + break; + case OpCode.MUL: + Mul(instruction); + break; + case OpCode.DIV: + Div(instruction); + break; + case OpCode.MOD: + Mod(instruction); + break; + case OpCode.POW: + Pow(instruction); + break; + case OpCode.SQRT: + Sqrt(instruction); + break; + case OpCode.MODMUL: + ModMul(instruction); + break; + case OpCode.MODPOW: + ModPow(instruction); + break; + case OpCode.SHL: + Shl(instruction); + break; + case OpCode.SHR: + Shr(instruction); + break; + case OpCode.NOT: + Not(instruction); + break; + case OpCode.BOOLAND: + BoolAnd(instruction); + break; + case OpCode.BOOLOR: + BoolOr(instruction); + break; + case OpCode.NZ: + Nz(instruction); + break; + case OpCode.NUMEQUAL: + NumEqual(instruction); + break; + case OpCode.NUMNOTEQUAL: + NumNotEqual(instruction); + break; + case OpCode.LT: + Lt(instruction); + break; + case OpCode.LE: + Le(instruction); + break; + case OpCode.GT: + Gt(instruction); + break; + case OpCode.GE: + Ge(instruction); + break; + case OpCode.MIN: + Min(instruction); + break; + case OpCode.MAX: + Max(instruction); + break; + case OpCode.WITHIN: + Within(instruction); + break; + + // Compound-type + case OpCode.PACKMAP: + PackMap(instruction); + break; + case OpCode.PACKSTRUCT: + PackStruct(instruction); + break; + case OpCode.PACK: + Pack(instruction); + break; + case OpCode.UNPACK: + Unpack(instruction); + break; + case OpCode.NEWARRAY0: + NewArray0(instruction); + break; + case OpCode.NEWARRAY: + case OpCode.NEWARRAY_T: + NewArray_T(instruction); + break; + case OpCode.NEWSTRUCT0: + NewStruct0(instruction); + break; + case OpCode.NEWSTRUCT: + NewStruct(instruction); + break; + case OpCode.NEWMAP: + NewMap(instruction); + break; + case OpCode.SIZE: + Size(instruction); + break; + case OpCode.HASKEY: + HasKey(instruction); + break; + case OpCode.KEYS: + Keys(instruction); + break; + case OpCode.VALUES: + Values(instruction); + break; + case OpCode.PICKITEM: + PickItem(instruction); + break; + case OpCode.APPEND: + Append(instruction); + break; + case OpCode.SETITEM: + SetItem(instruction); + break; + case OpCode.REVERSEITEMS: + ReverseItems(instruction); + break; + case OpCode.REMOVE: + Remove(instruction); + break; + case OpCode.CLEARITEMS: + ClearItems(instruction); + break; + case OpCode.POPITEM: + PopItem(instruction); + break; + + //Types + case OpCode.ISNULL: + IsNull(instruction); + break; + case OpCode.ISTYPE: + IsType(instruction); + break; + case OpCode.CONVERT: + Convert(instruction); + break; + case OpCode.ABORTMSG: + AbortMsg(instruction); + break; + case OpCode.ASSERTMSG: + AssertMsg(instruction); + break; + default: + throw new InvalidOperationException($"Opcode {instruction.OpCode} is undefined."); + } + */ + } + + } +} diff --git a/src/Neo.VM/OpcodeMethodAttribute.cs b/src/Neo.VM/OpcodeMethodAttribute.cs new file mode 100644 index 0000000000..3a91233728 --- /dev/null +++ b/src/Neo.VM/OpcodeMethodAttribute.cs @@ -0,0 +1,36 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// OpcodeMethodAttribute.cs file belongs to the neo project and is free +// software distributed under the MIT software license, see the +// accompanying file LICENSE in the main directory of the +// repository 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 +{ + /// + /// Indicates the . + /// + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + public class OpcodeMethodAttribute : Attribute + { + /// + /// The method is for this Opcode + /// + public OpCode OpCode { get; set; } + + /// + /// Constructor + /// + /// OpCode + public OpcodeMethodAttribute(OpCode opCode) + { + OpCode = opCode; + } + } +} From 4537325c8f9236e2816db58750747c8145fd629c Mon Sep 17 00:00:00 2001 From: Fernando Diaz Toledano Date: Tue, 6 Feb 2024 19:12:27 +0100 Subject: [PATCH 2/7] Types --- src/Neo.VM/JumpTable.Control.cs | 45 ----------------------- src/Neo.VM/JumpTable.Types.cs | 65 +++++++++++++++++++++++++++++++++ src/Neo.VM/JumpTable.cs | 40 ++++++-------------- 3 files changed, 76 insertions(+), 74 deletions(-) create mode 100644 src/Neo.VM/JumpTable.Types.cs diff --git a/src/Neo.VM/JumpTable.Control.cs b/src/Neo.VM/JumpTable.Control.cs index 0c2a2aa694..4015ef426c 100644 --- a/src/Neo.VM/JumpTable.Control.cs +++ b/src/Neo.VM/JumpTable.Control.cs @@ -17,72 +17,27 @@ public partial class JumpTable { /* TODO case OpCode.JMPGT: - JmpGt(instruction); - break; case OpCode.JMPGT_L: - JmpGt_L(instruction); - break; case OpCode.JMPGE: - JmpGe(instruction); - break; case OpCode.JMPGE_L: - JmpGe_L(instruction); - break; case OpCode.JMPLT: - JmpLt(instruction); - break; case OpCode.JMPLT_L: - JmpLt_L(instruction); - break; case OpCode.JMPLE: - JmpLe(instruction); - break; case OpCode.JMPLE_L: - JmpLe_L(instruction); - break; case OpCode.CALL: - Call(instruction); - break; case OpCode.CALL_L: - Call_L(instruction); - break; case OpCode.CALLA: - CallA(instruction); - break; case OpCode.CALLT: - CallT(instruction); - break; case OpCode.ABORT: - Abort(instruction); - break; case OpCode.ASSERT: - Assert(instruction); - break; case OpCode.THROW: - Throw(instruction); - break; case OpCode.TRY: - Try(instruction); - break; case OpCode.TRY_L: - Try_L(instruction); - break; case OpCode.ENDTRY: - EndTry(instruction); - break; case OpCode.ENDTRY_L: - EndTry_L(instruction); - break; case OpCode.ENDFINALLY: - EndFinally(instruction); - break; case OpCode.RET: - Ret(instruction); - break; case OpCode.SYSCALL: - Syscall(instruction); - break; - */ [OpcodeMethod(OpCode.NOP)] diff --git a/src/Neo.VM/JumpTable.Types.cs b/src/Neo.VM/JumpTable.Types.cs new file mode 100644 index 0000000000..b8c1da6cf3 --- /dev/null +++ b/src/Neo.VM/JumpTable.Types.cs @@ -0,0 +1,65 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// JumpTable.Types.cs file belongs to the neo project and is free +// software distributed under the MIT software license, see the +// accompanying file LICENSE in the main directory of the +// repository 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 Neo.VM.Types; +using System; +using System.Runtime.CompilerServices; + +namespace Neo.VM +{ + public partial class JumpTable + { + [OpcodeMethod(OpCode.ISNULL)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void IsNull(ExecutionEngine engine, Instruction instruction) + { + var x = engine.Pop(); + engine.Push(x.IsNull); + } + + [OpcodeMethod(OpCode.ISTYPE)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void IsType(ExecutionEngine engine, Instruction instruction) + { + var x = engine.Pop(); + var type = (StackItemType)instruction.TokenU8; + if (type == StackItemType.Any || !Enum.IsDefined(typeof(StackItemType), type)) + throw new InvalidOperationException($"Invalid type: {type}"); + engine.Push(x.Type == type); + } + + [OpcodeMethod(OpCode.CONVERT)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Convert(ExecutionEngine engine, Instruction instruction) + { + var x = engine.Pop(); + engine.Push(x.ConvertTo((StackItemType)instruction.TokenU8)); + } + + [OpcodeMethod(OpCode.ABORTMSG)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void AbortMsg(ExecutionEngine engine, Instruction instruction) + { + var msg = engine.Pop().GetString(); + throw new Exception($"{OpCode.ABORTMSG} is executed. Reason: {msg}"); + } + + [OpcodeMethod(OpCode.ASSERTMSG)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void AssertMsg(ExecutionEngine engine, Instruction instruction) + { + var msg = engine.Pop().GetString(); + var x = engine.Pop().GetBoolean(); + if (!x) + throw new Exception($"{OpCode.ASSERTMSG} is executed with false result. Reason: {msg}"); + } + } +} diff --git a/src/Neo.VM/JumpTable.cs b/src/Neo.VM/JumpTable.cs index 0a352567a0..2f89161d61 100644 --- a/src/Neo.VM/JumpTable.cs +++ b/src/Neo.VM/JumpTable.cs @@ -16,17 +16,17 @@ namespace Neo.VM { public partial class JumpTable { - public delegate void delAction(ExecutionEngine engine, Instruction instruction); - public readonly delAction[] Table = new delAction[byte.MaxValue]; + public delegate void DelAction(ExecutionEngine engine, Instruction instruction); + private readonly DelAction[] _table = new DelAction[byte.MaxValue]; /// /// Get Method /// /// OpCode /// Action - public delAction GetMethod(OpCode opCode) + public DelAction GetMethod(OpCode opCode) { - return Table[(byte)opCode]; + return _table[(byte)opCode]; } public JumpTable() @@ -37,28 +37,29 @@ public JumpTable() { foreach (var attr in mi.GetCustomAttributes(true)) { - if (Table[(byte)attr.OpCode] is not null) + if (_table[(byte)attr.OpCode] is not null) { throw new InvalidOperationException($"Opcode {attr.OpCode} is already defined."); } - Table[(byte)attr.OpCode] = (delAction)mi.CreateDelegate(typeof(delAction), this); + _table[(byte)attr.OpCode] = (DelAction)mi.CreateDelegate(typeof(DelAction), this); } } // Fill with undefined - for (int x = 0; x < Table.Length; x++) + for (int x = 0; x < _table.Length; x++) { - if (Table[x] is not null) continue; + if (_table[x] is not null) continue; - Table[x] = (engine, instruction) => + _table[x] = (engine, instruction) => { throw new InvalidOperationException($"Opcode {instruction.OpCode} is undefined."); }; } - /* + /* TODO: + switch (instruction.OpCode) { // Stack ops @@ -379,25 +380,6 @@ public JumpTable() case OpCode.POPITEM: PopItem(instruction); break; - - //Types - case OpCode.ISNULL: - IsNull(instruction); - break; - case OpCode.ISTYPE: - IsType(instruction); - break; - case OpCode.CONVERT: - Convert(instruction); - break; - case OpCode.ABORTMSG: - AbortMsg(instruction); - break; - case OpCode.ASSERTMSG: - AssertMsg(instruction); - break; - default: - throw new InvalidOperationException($"Opcode {instruction.OpCode} is undefined."); } */ } From d12b011a20b2672adec604f4710a466ebfe533fe Mon Sep 17 00:00:00 2001 From: Fernando Diaz Toledano Date: Tue, 6 Feb 2024 19:20:40 +0100 Subject: [PATCH 3/7] Splice --- src/Neo.VM/JumpTable.Splice.cs | 112 +++++++++++++++++++++++++++++++++ src/Neo.VM/JumpTable.cs | 22 +------ 2 files changed, 113 insertions(+), 21 deletions(-) create mode 100644 src/Neo.VM/JumpTable.Splice.cs diff --git a/src/Neo.VM/JumpTable.Splice.cs b/src/Neo.VM/JumpTable.Splice.cs new file mode 100644 index 0000000000..9ab770df51 --- /dev/null +++ b/src/Neo.VM/JumpTable.Splice.cs @@ -0,0 +1,112 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// JumpTable.Splice.cs file belongs to the neo project and is free +// software distributed under the MIT software license, see the +// accompanying file LICENSE in the main directory of the +// repository 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.Runtime.CompilerServices; + +namespace Neo.VM +{ + public partial class JumpTable + { + [OpcodeMethod(OpCode.NEWBUFFER)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void NewBuffer(ExecutionEngine engine, Instruction instruction) + { + int length = (int)engine.Pop().GetInteger(); + engine.Limits.AssertMaxItemSize(length); + engine.Push(new Types.Buffer(length)); + } + + [OpcodeMethod(OpCode.MEMCPY)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Memcpy(ExecutionEngine engine, Instruction instruction) + { + int count = (int)engine.Pop().GetInteger(); + if (count < 0) + throw new InvalidOperationException($"The value {count} is out of range."); + int si = (int)engine.Pop().GetInteger(); + if (si < 0) + throw new InvalidOperationException($"The value {si} is out of range."); + ReadOnlySpan src = engine.Pop().GetSpan(); + if (checked(si + count) > src.Length) + throw new InvalidOperationException($"The value {count} is out of range."); + int di = (int)engine.Pop().GetInteger(); + if (di < 0) + throw new InvalidOperationException($"The value {di} is out of range."); + Types.Buffer dst = engine.Pop(); + if (checked(di + count) > dst.Size) + throw new InvalidOperationException($"The value {count} is out of range."); + src.Slice(si, count).CopyTo(dst.InnerBuffer.Span[di..]); + } + + [OpcodeMethod(OpCode.CAT)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Cat(ExecutionEngine engine, Instruction instruction) + { + var x2 = engine.Pop().GetSpan(); + var x1 = engine.Pop().GetSpan(); + int length = x1.Length + x2.Length; + engine.Limits.AssertMaxItemSize(length); + Types.Buffer result = new(length, false); + x1.CopyTo(result.InnerBuffer.Span); + x2.CopyTo(result.InnerBuffer.Span[x1.Length..]); + engine.Push(result); + } + + [OpcodeMethod(OpCode.SUBSTR)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Substr(ExecutionEngine engine, Instruction instruction) + { + int count = (int)engine.Pop().GetInteger(); + if (count < 0) + throw new InvalidOperationException($"The value {count} is out of range."); + int index = (int)engine.Pop().GetInteger(); + if (index < 0) + throw new InvalidOperationException($"The value {index} is out of range."); + var x = engine.Pop().GetSpan(); + if (index + count > x.Length) + throw new InvalidOperationException($"The value {count} is out of range."); + Types.Buffer result = new(count, false); + x.Slice(index, count).CopyTo(result.InnerBuffer.Span); + engine.Push(result); + } + + [OpcodeMethod(OpCode.LEFT)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Left(ExecutionEngine engine, Instruction instruction) + { + int count = (int)engine.Pop().GetInteger(); + if (count < 0) + throw new InvalidOperationException($"The value {count} is out of range."); + var x = engine.Pop().GetSpan(); + if (count > x.Length) + throw new InvalidOperationException($"The value {count} is out of range."); + Types.Buffer result = new(count, false); + x[..count].CopyTo(result.InnerBuffer.Span); + engine.Push(result); + } + + [OpcodeMethod(OpCode.RIGHT)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Right(ExecutionEngine engine, Instruction instruction) + { + int count = (int)engine.Pop().GetInteger(); + if (count < 0) + throw new InvalidOperationException($"The value {count} is out of range."); + var x = engine.Pop().GetSpan(); + if (count > x.Length) + throw new InvalidOperationException($"The value {count} is out of range."); + Types.Buffer result = new(count, false); + x[^count..^0].CopyTo(result.InnerBuffer.Span); + engine.Push(result); + } + } +} diff --git a/src/Neo.VM/JumpTable.cs b/src/Neo.VM/JumpTable.cs index 2f89161d61..ca0e0e9a50 100644 --- a/src/Neo.VM/JumpTable.cs +++ b/src/Neo.VM/JumpTable.cs @@ -17,7 +17,7 @@ namespace Neo.VM public partial class JumpTable { public delegate void DelAction(ExecutionEngine engine, Instruction instruction); - private readonly DelAction[] _table = new DelAction[byte.MaxValue]; + protected readonly DelAction[] _table = new DelAction[byte.MaxValue]; /// /// Get Method @@ -189,26 +189,6 @@ public JumpTable() StArg(instruction); break; - // Splice - case OpCode.NEWBUFFER: - NewBuffer(instruction); - break; - case OpCode.MEMCPY: - Memcpy(instruction); - break; - case OpCode.CAT: - Cat(instruction); - break; - case OpCode.SUBSTR: - Substr(instruction); - break; - case OpCode.LEFT: - Left(instruction); - break; - case OpCode.RIGHT: - Right(instruction); - break; - // Bitwise logic case OpCode.INVERT: Invert(instruction); From 23d4be4589e5dbfa1c9bcf3c099933d577efe44a Mon Sep 17 00:00:00 2001 From: Fernando Diaz Toledano Date: Tue, 6 Feb 2024 19:22:42 +0100 Subject: [PATCH 4/7] revert partial --- src/Neo.VM/ExecutionEngine.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Neo.VM/ExecutionEngine.cs b/src/Neo.VM/ExecutionEngine.cs index f020aa24e5..02507de608 100644 --- a/src/Neo.VM/ExecutionEngine.cs +++ b/src/Neo.VM/ExecutionEngine.cs @@ -19,7 +19,7 @@ namespace Neo.VM /// /// Represents the VM used to execute the script. /// - public partial class ExecutionEngine : IDisposable + public class ExecutionEngine : IDisposable { private VMState state = VMState.BREAK; private bool isJumping = false; From 2e41b1ebca56a8c0f1d7376f3eb0d98806af2135 Mon Sep 17 00:00:00 2001 From: Fernando Diaz Toledano Date: Tue, 6 Feb 2024 19:30:03 +0100 Subject: [PATCH 5/7] More control --- src/Neo.VM/ExecutionEngine.cs | 2 +- src/Neo.VM/JumpTable.Control.cs | 134 ++++++++++++++++++++++++++------ 2 files changed, 110 insertions(+), 26 deletions(-) diff --git a/src/Neo.VM/ExecutionEngine.cs b/src/Neo.VM/ExecutionEngine.cs index 02507de608..67fe4bb5d7 100644 --- a/src/Neo.VM/ExecutionEngine.cs +++ b/src/Neo.VM/ExecutionEngine.cs @@ -142,7 +142,7 @@ public virtual VMState Execute() } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void ExecuteCall(int position) + public void ExecuteCall(int position) { LoadContext(CurrentContext!.Clone(position)); } diff --git a/src/Neo.VM/JumpTable.Control.cs b/src/Neo.VM/JumpTable.Control.cs index 4015ef426c..eef9b09659 100644 --- a/src/Neo.VM/JumpTable.Control.cs +++ b/src/Neo.VM/JumpTable.Control.cs @@ -15,31 +15,6 @@ namespace Neo.VM { public partial class JumpTable { - /* TODO - case OpCode.JMPGT: - case OpCode.JMPGT_L: - case OpCode.JMPGE: - case OpCode.JMPGE_L: - case OpCode.JMPLT: - case OpCode.JMPLT_L: - case OpCode.JMPLE: - case OpCode.JMPLE_L: - case OpCode.CALL: - case OpCode.CALL_L: - case OpCode.CALLA: - case OpCode.CALLT: - case OpCode.ABORT: - case OpCode.ASSERT: - case OpCode.THROW: - case OpCode.TRY: - case OpCode.TRY_L: - case OpCode.ENDTRY: - case OpCode.ENDTRY_L: - case OpCode.ENDFINALLY: - case OpCode.RET: - case OpCode.SYSCALL: - */ - [OpcodeMethod(OpCode.NOP)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public virtual void Nop(ExecutionEngine engine, Instruction instruction) @@ -131,5 +106,114 @@ public virtual void JmpNeL(ExecutionEngine engine, Instruction instruction) if (x1 != x2) engine.ExecuteJumpOffset(instruction.TokenI32); } + + [OpcodeMethod(OpCode.JMPGT)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void JmpGt(ExecutionEngine engine, Instruction instruction) + { + var x2 = engine.Pop().GetInteger(); + var x1 = engine.Pop().GetInteger(); + if (x1 > x2) + engine.ExecuteJumpOffset(instruction.TokenI8); + } + + [OpcodeMethod(OpCode.JMPGT_L)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void JmpGtL(ExecutionEngine engine, Instruction instruction) + { + var x2 = engine.Pop().GetInteger(); + var x1 = engine.Pop().GetInteger(); + if (x1 > x2) + engine.ExecuteJumpOffset(instruction.TokenI32); + } + + [OpcodeMethod(OpCode.JMPGE)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void JmpGe(ExecutionEngine engine, Instruction instruction) + { + var x2 = engine.Pop().GetInteger(); + var x1 = engine.Pop().GetInteger(); + if (x1 >= x2) + engine.ExecuteJumpOffset(instruction.TokenI8); + } + + [OpcodeMethod(OpCode.JMPGE_L)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void JmpGeL(ExecutionEngine engine, Instruction instruction) + { + var x2 = engine.Pop().GetInteger(); + var x1 = engine.Pop().GetInteger(); + if (x1 >= x2) + engine.ExecuteJumpOffset(instruction.TokenI32); + } + + [OpcodeMethod(OpCode.JMPLT)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void JmpLt(ExecutionEngine engine, Instruction instruction) + { + var x2 = engine.Pop().GetInteger(); + var x1 = engine.Pop().GetInteger(); + if (x1 < x2) + engine.ExecuteJumpOffset(instruction.TokenI8); + } + + [OpcodeMethod(OpCode.JMPLT_L)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void JmpLtL(ExecutionEngine engine, Instruction instruction) + { + var x2 = engine.Pop().GetInteger(); + var x1 = engine.Pop().GetInteger(); + if (x1 < x2) + engine.ExecuteJumpOffset(instruction.TokenI32); + } + + [OpcodeMethod(OpCode.JMPLE)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void JmpLe(ExecutionEngine engine, Instruction instruction) + { + var x2 = engine.Pop().GetInteger(); + var x1 = engine.Pop().GetInteger(); + if (x1 <= x2) + engine.ExecuteJumpOffset(instruction.TokenI8); + } + + [OpcodeMethod(OpCode.JMPLE_L)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void JmpLeL(ExecutionEngine engine, Instruction instruction) + { + var x2 = engine.Pop().GetInteger(); + var x1 = engine.Pop().GetInteger(); + if (x1 <= x2) + engine.ExecuteJumpOffset(instruction.TokenI32); + } + + [OpcodeMethod(OpCode.CALL)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Call(ExecutionEngine engine, Instruction instruction) + { + engine.ExecuteCall(checked(engine.CurrentContext!.InstructionPointer + instruction.TokenI8)); + } + + [OpcodeMethod(OpCode.CALL_L)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void CallL(ExecutionEngine engine, Instruction instruction) + { + engine.ExecuteCall(checked(engine.CurrentContext!.InstructionPointer + instruction.TokenI32)); + } + + /* TODO + case OpCode.CALLA: + case OpCode.CALLT: + case OpCode.ABORT: + case OpCode.ASSERT: + case OpCode.THROW: + case OpCode.TRY: + case OpCode.TRY_L: + case OpCode.ENDTRY: + case OpCode.ENDTRY_L: + case OpCode.ENDFINALLY: + case OpCode.RET: + case OpCode.SYSCALL: + */ } } From 8f7809bd85811868bcede2aa122f09391ea06a26 Mon Sep 17 00:00:00 2001 From: Fernando Diaz Toledano Date: Tue, 6 Feb 2024 19:39:35 +0100 Subject: [PATCH 6/7] Remove attribute --- src/Neo.VM/JumpTable.Control.cs | 63 +++------ src/Neo.VM/JumpTable.Push.cs | 197 ++++++++++++++++++++++------ src/Neo.VM/JumpTable.Splice.cs | 18 +-- src/Neo.VM/JumpTable.Types.cs | 16 +-- src/Neo.VM/JumpTable.cs | 9 +- src/Neo.VM/OpcodeMethodAttribute.cs | 36 ----- 6 files changed, 192 insertions(+), 147 deletions(-) delete mode 100644 src/Neo.VM/OpcodeMethodAttribute.cs diff --git a/src/Neo.VM/JumpTable.Control.cs b/src/Neo.VM/JumpTable.Control.cs index eef9b09659..5d5d172c3c 100644 --- a/src/Neo.VM/JumpTable.Control.cs +++ b/src/Neo.VM/JumpTable.Control.cs @@ -15,61 +15,53 @@ namespace Neo.VM { public partial class JumpTable { - [OpcodeMethod(OpCode.NOP)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public virtual void Nop(ExecutionEngine engine, Instruction instruction) + public virtual void NOP(ExecutionEngine engine, Instruction instruction) { } - [OpcodeMethod(OpCode.JMP)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public virtual void Jmp(ExecutionEngine engine, Instruction instruction) + public virtual void JMP(ExecutionEngine engine, Instruction instruction) { engine.ExecuteJumpOffset(instruction.TokenI8); } - [OpcodeMethod(OpCode.JMP_L)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public virtual void JmpL(ExecutionEngine engine, Instruction instruction) + public virtual void JMP_L(ExecutionEngine engine, Instruction instruction) { engine.ExecuteJumpOffset(instruction.TokenI32); } - [OpcodeMethod(OpCode.JMPIF)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public virtual void JmpIf(ExecutionEngine engine, Instruction instruction) + public virtual void JMPIF(ExecutionEngine engine, Instruction instruction) { if (engine.Pop().GetBoolean()) engine.ExecuteJumpOffset(instruction.TokenI8); } - [OpcodeMethod(OpCode.JMPIF_L)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public virtual void JmpIfL(ExecutionEngine engine, Instruction instruction) + public virtual void JMPIF_L(ExecutionEngine engine, Instruction instruction) { if (engine.Pop().GetBoolean()) engine.ExecuteJumpOffset(instruction.TokenI32); } - [OpcodeMethod(OpCode.JMPIFNOT)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public virtual void JmpIfNot(ExecutionEngine engine, Instruction instruction) + public virtual void JMPIFNOT(ExecutionEngine engine, Instruction instruction) { if (!engine.Pop().GetBoolean()) engine.ExecuteJumpOffset(instruction.TokenI8); } - [OpcodeMethod(OpCode.JMPIFNOT_L)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public virtual void JmpIfNotL(ExecutionEngine engine, Instruction instruction) + public virtual void JMPIFNOT_L(ExecutionEngine engine, Instruction instruction) { if (!engine.Pop().GetBoolean()) engine.ExecuteJumpOffset(instruction.TokenI32); } - [OpcodeMethod(OpCode.JMPEQ)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public virtual void JmpEq(ExecutionEngine engine, Instruction instruction) + public virtual void JMPEQ(ExecutionEngine engine, Instruction instruction) { var x2 = engine.Pop().GetInteger(); var x1 = engine.Pop().GetInteger(); @@ -77,9 +69,8 @@ public virtual void JmpEq(ExecutionEngine engine, Instruction instruction) engine.ExecuteJumpOffset(instruction.TokenI8); } - [OpcodeMethod(OpCode.JMPEQ_L)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public virtual void JmpEqL(ExecutionEngine engine, Instruction instruction) + public virtual void JMPEQ_L(ExecutionEngine engine, Instruction instruction) { var x2 = engine.Pop().GetInteger(); var x1 = engine.Pop().GetInteger(); @@ -87,9 +78,8 @@ public virtual void JmpEqL(ExecutionEngine engine, Instruction instruction) engine.ExecuteJumpOffset(instruction.TokenI32); } - [OpcodeMethod(OpCode.JMPNE)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public virtual void JmpNe(ExecutionEngine engine, Instruction instruction) + public virtual void JMPNE(ExecutionEngine engine, Instruction instruction) { var x2 = engine.Pop().GetInteger(); var x1 = engine.Pop().GetInteger(); @@ -97,9 +87,8 @@ public virtual void JmpNe(ExecutionEngine engine, Instruction instruction) engine.ExecuteJumpOffset(instruction.TokenI8); } - [OpcodeMethod(OpCode.JMPNE_L)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public virtual void JmpNeL(ExecutionEngine engine, Instruction instruction) + public virtual void JMPNE_L(ExecutionEngine engine, Instruction instruction) { var x2 = engine.Pop().GetInteger(); var x1 = engine.Pop().GetInteger(); @@ -107,9 +96,8 @@ public virtual void JmpNeL(ExecutionEngine engine, Instruction instruction) engine.ExecuteJumpOffset(instruction.TokenI32); } - [OpcodeMethod(OpCode.JMPGT)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public virtual void JmpGt(ExecutionEngine engine, Instruction instruction) + public virtual void JMPGT(ExecutionEngine engine, Instruction instruction) { var x2 = engine.Pop().GetInteger(); var x1 = engine.Pop().GetInteger(); @@ -117,9 +105,8 @@ public virtual void JmpGt(ExecutionEngine engine, Instruction instruction) engine.ExecuteJumpOffset(instruction.TokenI8); } - [OpcodeMethod(OpCode.JMPGT_L)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public virtual void JmpGtL(ExecutionEngine engine, Instruction instruction) + public virtual void JMPGT_L(ExecutionEngine engine, Instruction instruction) { var x2 = engine.Pop().GetInteger(); var x1 = engine.Pop().GetInteger(); @@ -127,9 +114,8 @@ public virtual void JmpGtL(ExecutionEngine engine, Instruction instruction) engine.ExecuteJumpOffset(instruction.TokenI32); } - [OpcodeMethod(OpCode.JMPGE)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public virtual void JmpGe(ExecutionEngine engine, Instruction instruction) + public virtual void JMPGE(ExecutionEngine engine, Instruction instruction) { var x2 = engine.Pop().GetInteger(); var x1 = engine.Pop().GetInteger(); @@ -137,9 +123,8 @@ public virtual void JmpGe(ExecutionEngine engine, Instruction instruction) engine.ExecuteJumpOffset(instruction.TokenI8); } - [OpcodeMethod(OpCode.JMPGE_L)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public virtual void JmpGeL(ExecutionEngine engine, Instruction instruction) + public virtual void JMPGE_L(ExecutionEngine engine, Instruction instruction) { var x2 = engine.Pop().GetInteger(); var x1 = engine.Pop().GetInteger(); @@ -147,9 +132,8 @@ public virtual void JmpGeL(ExecutionEngine engine, Instruction instruction) engine.ExecuteJumpOffset(instruction.TokenI32); } - [OpcodeMethod(OpCode.JMPLT)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public virtual void JmpLt(ExecutionEngine engine, Instruction instruction) + public virtual void JMPLT(ExecutionEngine engine, Instruction instruction) { var x2 = engine.Pop().GetInteger(); var x1 = engine.Pop().GetInteger(); @@ -157,9 +141,8 @@ public virtual void JmpLt(ExecutionEngine engine, Instruction instruction) engine.ExecuteJumpOffset(instruction.TokenI8); } - [OpcodeMethod(OpCode.JMPLT_L)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public virtual void JmpLtL(ExecutionEngine engine, Instruction instruction) + public virtual void JMPLT_L(ExecutionEngine engine, Instruction instruction) { var x2 = engine.Pop().GetInteger(); var x1 = engine.Pop().GetInteger(); @@ -167,9 +150,8 @@ public virtual void JmpLtL(ExecutionEngine engine, Instruction instruction) engine.ExecuteJumpOffset(instruction.TokenI32); } - [OpcodeMethod(OpCode.JMPLE)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public virtual void JmpLe(ExecutionEngine engine, Instruction instruction) + public virtual void JMPLE(ExecutionEngine engine, Instruction instruction) { var x2 = engine.Pop().GetInteger(); var x1 = engine.Pop().GetInteger(); @@ -177,9 +159,8 @@ public virtual void JmpLe(ExecutionEngine engine, Instruction instruction) engine.ExecuteJumpOffset(instruction.TokenI8); } - [OpcodeMethod(OpCode.JMPLE_L)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public virtual void JmpLeL(ExecutionEngine engine, Instruction instruction) + public virtual void JMPLE_L(ExecutionEngine engine, Instruction instruction) { var x2 = engine.Pop().GetInteger(); var x1 = engine.Pop().GetInteger(); @@ -187,16 +168,14 @@ public virtual void JmpLeL(ExecutionEngine engine, Instruction instruction) engine.ExecuteJumpOffset(instruction.TokenI32); } - [OpcodeMethod(OpCode.CALL)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public virtual void Call(ExecutionEngine engine, Instruction instruction) + public virtual void CALL(ExecutionEngine engine, Instruction instruction) { engine.ExecuteCall(checked(engine.CurrentContext!.InstructionPointer + instruction.TokenI8)); } - [OpcodeMethod(OpCode.CALL_L)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public virtual void CallL(ExecutionEngine engine, Instruction instruction) + public virtual void CALL_L(ExecutionEngine engine, Instruction instruction) { engine.ExecuteCall(checked(engine.CurrentContext!.InstructionPointer + instruction.TokenI32)); } diff --git a/src/Neo.VM/JumpTable.Push.cs b/src/Neo.VM/JumpTable.Push.cs index e15910778a..66b9f64c51 100644 --- a/src/Neo.VM/JumpTable.Push.cs +++ b/src/Neo.VM/JumpTable.Push.cs @@ -18,35 +18,56 @@ namespace Neo.VM { public partial class JumpTable { - [OpcodeMethod(OpCode.PUSHINT8)] - [OpcodeMethod(OpCode.PUSHINT16)] - [OpcodeMethod(OpCode.PUSHINT32)] - [OpcodeMethod(OpCode.PUSHINT64)] - [OpcodeMethod(OpCode.PUSHINT128)] - [OpcodeMethod(OpCode.PUSHINT256)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public virtual void PushInt(ExecutionEngine engine, Instruction instruction) + public virtual void PUSHINT8(ExecutionEngine engine, Instruction instruction) { engine.Push(new BigInteger(instruction.Operand.Span)); } - [OpcodeMethod(OpCode.PUSHT)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public virtual void PushT(ExecutionEngine engine, Instruction instruction) + public virtual void PUSHINT16(ExecutionEngine engine, Instruction instruction) + { + engine.Push(new BigInteger(instruction.Operand.Span)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void PUSHINT32(ExecutionEngine engine, Instruction instruction) + { + engine.Push(new BigInteger(instruction.Operand.Span)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void PUSHINT64(ExecutionEngine engine, Instruction instruction) + { + engine.Push(new BigInteger(instruction.Operand.Span)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void PUSHINT128(ExecutionEngine engine, Instruction instruction) + { + engine.Push(new BigInteger(instruction.Operand.Span)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void PUSHINT256(ExecutionEngine engine, Instruction instruction) + { + engine.Push(new BigInteger(instruction.Operand.Span)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void PUSHT(ExecutionEngine engine, Instruction instruction) { engine.Push(StackItem.True); } - [OpcodeMethod(OpCode.PUSHF)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public virtual void PushF(ExecutionEngine engine, Instruction instruction) + public virtual void PUSHF(ExecutionEngine engine, Instruction instruction) { engine.Push(StackItem.False); } - [OpcodeMethod(OpCode.PUSHA)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public virtual void PushA(ExecutionEngine engine, Instruction instruction) + public virtual void PUSHA(ExecutionEngine engine, Instruction instruction) { var position = checked(engine.CurrentContext!.InstructionPointer + instruction.TokenI32); if (position < 0 || position > engine.CurrentContext.Script.Length) @@ -54,45 +75,139 @@ public virtual void PushA(ExecutionEngine engine, Instruction instruction) engine.Push(new Pointer(engine.CurrentContext.Script, position)); } - [OpcodeMethod(OpCode.PUSHNULL)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public virtual void PushNull(ExecutionEngine engine, Instruction instruction) + public virtual void PUSHNULL(ExecutionEngine engine, Instruction instruction) { engine.Push(StackItem.Null); } - [OpcodeMethod(OpCode.PUSHDATA1)] - [OpcodeMethod(OpCode.PUSHDATA2)] - [OpcodeMethod(OpCode.PUSHDATA4)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public virtual void PushData(ExecutionEngine engine, Instruction instruction) + public virtual void PUSHDATA1(ExecutionEngine engine, Instruction instruction) + { + engine.Limits.AssertMaxItemSize(instruction.Operand.Length); + engine.Push(instruction.Operand); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void PUSHDATA2(ExecutionEngine engine, Instruction instruction) + { + engine.Limits.AssertMaxItemSize(instruction.Operand.Length); + engine.Push(instruction.Operand); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void PUSHDATA4(ExecutionEngine engine, Instruction instruction) { engine.Limits.AssertMaxItemSize(instruction.Operand.Length); engine.Push(instruction.Operand); } - [OpcodeMethod(OpCode.PUSHM1)] - [OpcodeMethod(OpCode.PUSH0)] - [OpcodeMethod(OpCode.PUSH1)] - [OpcodeMethod(OpCode.PUSH2)] - [OpcodeMethod(OpCode.PUSH3)] - [OpcodeMethod(OpCode.PUSH4)] - [OpcodeMethod(OpCode.PUSH5)] - [OpcodeMethod(OpCode.PUSH6)] - [OpcodeMethod(OpCode.PUSH7)] - [OpcodeMethod(OpCode.PUSH8)] - [OpcodeMethod(OpCode.PUSH9)] - [OpcodeMethod(OpCode.PUSH10)] - [OpcodeMethod(OpCode.PUSH11)] - [OpcodeMethod(OpCode.PUSH12)] - [OpcodeMethod(OpCode.PUSH13)] - [OpcodeMethod(OpCode.PUSH14)] - [OpcodeMethod(OpCode.PUSH15)] - [OpcodeMethod(OpCode.PUSH16)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public virtual void Push(ExecutionEngine engine, Instruction instruction) - { - engine.Push((int)instruction.OpCode - (int)OpCode.PUSH0); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void PUSHM1(ExecutionEngine engine, Instruction instruction) + { + engine.Push(-1); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void PUSH0(ExecutionEngine engine, Instruction instruction) + { + engine.Push(0); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void PUSH1(ExecutionEngine engine, Instruction instruction) + { + engine.Push(1); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void PUSH2(ExecutionEngine engine, Instruction instruction) + { + engine.Push(2); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void PUSH3(ExecutionEngine engine, Instruction instruction) + { + engine.Push(3); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void PUSH4(ExecutionEngine engine, Instruction instruction) + { + engine.Push(4); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void PUSH5(ExecutionEngine engine, Instruction instruction) + { + engine.Push(5); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void PUSH6(ExecutionEngine engine, Instruction instruction) + { + engine.Push(6); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void PUSH7(ExecutionEngine engine, Instruction instruction) + { + engine.Push(7); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void PUSH8(ExecutionEngine engine, Instruction instruction) + { + engine.Push(8); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void PUSH9(ExecutionEngine engine, Instruction instruction) + { + engine.Push(9); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void PUSH10(ExecutionEngine engine, Instruction instruction) + { + engine.Push(10); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void PUSH11(ExecutionEngine engine, Instruction instruction) + { + engine.Push(11); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void PUSH12(ExecutionEngine engine, Instruction instruction) + { + engine.Push(12); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void PUSH13(ExecutionEngine engine, Instruction instruction) + { + engine.Push(13); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void PUSH14(ExecutionEngine engine, Instruction instruction) + { + engine.Push(14); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void PUSH15(ExecutionEngine engine, Instruction instruction) + { + engine.Push(15); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void PUSH16(ExecutionEngine engine, Instruction instruction) + { + engine.Push(16); } } } diff --git a/src/Neo.VM/JumpTable.Splice.cs b/src/Neo.VM/JumpTable.Splice.cs index 9ab770df51..f5aec89352 100644 --- a/src/Neo.VM/JumpTable.Splice.cs +++ b/src/Neo.VM/JumpTable.Splice.cs @@ -16,18 +16,16 @@ namespace Neo.VM { public partial class JumpTable { - [OpcodeMethod(OpCode.NEWBUFFER)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public virtual void NewBuffer(ExecutionEngine engine, Instruction instruction) + public virtual void NEWBUFFER(ExecutionEngine engine, Instruction instruction) { int length = (int)engine.Pop().GetInteger(); engine.Limits.AssertMaxItemSize(length); engine.Push(new Types.Buffer(length)); } - [OpcodeMethod(OpCode.MEMCPY)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public virtual void Memcpy(ExecutionEngine engine, Instruction instruction) + public virtual void MEMCPY(ExecutionEngine engine, Instruction instruction) { int count = (int)engine.Pop().GetInteger(); if (count < 0) @@ -47,9 +45,8 @@ public virtual void Memcpy(ExecutionEngine engine, Instruction instruction) src.Slice(si, count).CopyTo(dst.InnerBuffer.Span[di..]); } - [OpcodeMethod(OpCode.CAT)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public virtual void Cat(ExecutionEngine engine, Instruction instruction) + public virtual void CAT(ExecutionEngine engine, Instruction instruction) { var x2 = engine.Pop().GetSpan(); var x1 = engine.Pop().GetSpan(); @@ -61,9 +58,8 @@ public virtual void Cat(ExecutionEngine engine, Instruction instruction) engine.Push(result); } - [OpcodeMethod(OpCode.SUBSTR)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public virtual void Substr(ExecutionEngine engine, Instruction instruction) + public virtual void SUBSTR(ExecutionEngine engine, Instruction instruction) { int count = (int)engine.Pop().GetInteger(); if (count < 0) @@ -79,9 +75,8 @@ public virtual void Substr(ExecutionEngine engine, Instruction instruction) engine.Push(result); } - [OpcodeMethod(OpCode.LEFT)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public virtual void Left(ExecutionEngine engine, Instruction instruction) + public virtual void LEFT(ExecutionEngine engine, Instruction instruction) { int count = (int)engine.Pop().GetInteger(); if (count < 0) @@ -94,9 +89,8 @@ public virtual void Left(ExecutionEngine engine, Instruction instruction) engine.Push(result); } - [OpcodeMethod(OpCode.RIGHT)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public virtual void Right(ExecutionEngine engine, Instruction instruction) + public virtual void RIGHT(ExecutionEngine engine, Instruction instruction) { int count = (int)engine.Pop().GetInteger(); if (count < 0) diff --git a/src/Neo.VM/JumpTable.Types.cs b/src/Neo.VM/JumpTable.Types.cs index b8c1da6cf3..46fd50749d 100644 --- a/src/Neo.VM/JumpTable.Types.cs +++ b/src/Neo.VM/JumpTable.Types.cs @@ -17,17 +17,14 @@ namespace Neo.VM { public partial class JumpTable { - [OpcodeMethod(OpCode.ISNULL)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public virtual void IsNull(ExecutionEngine engine, Instruction instruction) + public virtual void ISNULL(ExecutionEngine engine, Instruction instruction) { var x = engine.Pop(); engine.Push(x.IsNull); } - [OpcodeMethod(OpCode.ISTYPE)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public virtual void IsType(ExecutionEngine engine, Instruction instruction) + public virtual void ISTYPE(ExecutionEngine engine, Instruction instruction) { var x = engine.Pop(); var type = (StackItemType)instruction.TokenU8; @@ -36,25 +33,22 @@ public virtual void IsType(ExecutionEngine engine, Instruction instruction) engine.Push(x.Type == type); } - [OpcodeMethod(OpCode.CONVERT)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public virtual void Convert(ExecutionEngine engine, Instruction instruction) + public virtual void CONVERT(ExecutionEngine engine, Instruction instruction) { var x = engine.Pop(); engine.Push(x.ConvertTo((StackItemType)instruction.TokenU8)); } - [OpcodeMethod(OpCode.ABORTMSG)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public virtual void AbortMsg(ExecutionEngine engine, Instruction instruction) + public virtual void ABORTMSG(ExecutionEngine engine, Instruction instruction) { var msg = engine.Pop().GetString(); throw new Exception($"{OpCode.ABORTMSG} is executed. Reason: {msg}"); } - [OpcodeMethod(OpCode.ASSERTMSG)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public virtual void AssertMsg(ExecutionEngine engine, Instruction instruction) + public virtual void ASSERTMSG(ExecutionEngine engine, Instruction instruction) { var msg = engine.Pop().GetString(); var x = engine.Pop().GetBoolean(); diff --git a/src/Neo.VM/JumpTable.cs b/src/Neo.VM/JumpTable.cs index ca0e0e9a50..db68fe71c8 100644 --- a/src/Neo.VM/JumpTable.cs +++ b/src/Neo.VM/JumpTable.cs @@ -10,7 +10,6 @@ // modifications are permitted. using System; -using System.Reflection; namespace Neo.VM { @@ -35,14 +34,14 @@ public JumpTable() foreach (var mi in GetType().GetMethods()) { - foreach (var attr in mi.GetCustomAttributes(true)) + if (Enum.TryParse(mi.Name, false, out var opCode)) { - if (_table[(byte)attr.OpCode] is not null) + if (_table[(byte)opCode] is not null) { - throw new InvalidOperationException($"Opcode {attr.OpCode} is already defined."); + throw new InvalidOperationException($"Opcode {opCode} is already defined."); } - _table[(byte)attr.OpCode] = (DelAction)mi.CreateDelegate(typeof(DelAction), this); + _table[(byte)opCode] = (DelAction)mi.CreateDelegate(typeof(DelAction), this); } } diff --git a/src/Neo.VM/OpcodeMethodAttribute.cs b/src/Neo.VM/OpcodeMethodAttribute.cs deleted file mode 100644 index 3a91233728..0000000000 --- a/src/Neo.VM/OpcodeMethodAttribute.cs +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (C) 2015-2024 The Neo Project. -// -// OpcodeMethodAttribute.cs file belongs to the neo project and is free -// software distributed under the MIT software license, see the -// accompanying file LICENSE in the main directory of the -// repository 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 -{ - /// - /// Indicates the . - /// - [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] - public class OpcodeMethodAttribute : Attribute - { - /// - /// The method is for this Opcode - /// - public OpCode OpCode { get; set; } - - /// - /// Constructor - /// - /// OpCode - public OpcodeMethodAttribute(OpCode opCode) - { - OpCode = opCode; - } - } -} From 957e1d2482074aaf829f2eeccae1d43ada0cfe1f Mon Sep 17 00:00:00 2001 From: Fernando Diaz Toledano Date: Wed, 7 Feb 2024 10:43:31 +0100 Subject: [PATCH 7/7] ApplicationEngine logic --- src/Neo.VM/ExecutionEngine.cs | 198 +++------------ src/Neo.VM/JumpTable.Control.cs | 270 ++++++++++++++++++--- src/Neo.VM/JumpTable.cs | 16 +- src/Neo/SmartContract/ApplicationEngine.cs | 81 +++++-- tests/Neo.VM.Tests/Types/TestEngine.cs | 19 +- 5 files changed, 346 insertions(+), 238 deletions(-) diff --git a/src/Neo.VM/ExecutionEngine.cs b/src/Neo.VM/ExecutionEngine.cs index 67fe4bb5d7..043b095ef0 100644 --- a/src/Neo.VM/ExecutionEngine.cs +++ b/src/Neo.VM/ExecutionEngine.cs @@ -22,8 +22,9 @@ namespace Neo.VM public class ExecutionEngine : IDisposable { private VMState state = VMState.BREAK; - private bool isJumping = false; - private readonly JumpTable JumpTable; + internal bool isJumping = false; + + public JumpTable JumpTable { get; } /// /// Restrictions on the VM. @@ -58,7 +59,7 @@ public class ExecutionEngine : IDisposable /// /// The VM object representing the uncaught exception. /// - public StackItem? UncaughtException { get; private set; } + public StackItem? UncaughtException { get; internal set; } /// /// The current state of the VM. @@ -100,29 +101,6 @@ protected ExecutionEngine(JumpTable? jumpTable, ReferenceCounter referenceCounte this.ResultStack = new EvaluationStack(referenceCounter); } - /// - /// Called when a context is unloaded. - /// - /// The context being unloaded. - protected virtual void ContextUnloaded(ExecutionContext context) - { - if (InvocationStack.Count == 0) - { - CurrentContext = null; - EntryContext = null; - } - else - { - CurrentContext = InvocationStack.Peek(); - } - if (context.StaticFields != null && context.StaticFields != CurrentContext?.StaticFields) - { - context.StaticFields.ClearReferences(); - } - context.LocalVariables?.ClearReferences(); - context.Arguments?.ClearReferences(); - } - public virtual void Dispose() { InvocationStack.Clear(); @@ -141,60 +119,6 @@ public virtual VMState Execute() return State; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ExecuteCall(int position) - { - LoadContext(CurrentContext!.Clone(position)); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void ExecuteEndTry(int endOffset) - { - if (CurrentContext!.TryStack is null) - throw new InvalidOperationException($"The corresponding TRY block cannot be found."); - if (!CurrentContext.TryStack.TryPeek(out ExceptionHandlingContext? currentTry)) - throw new InvalidOperationException($"The corresponding TRY block cannot be found."); - if (currentTry.State == ExceptionHandlingState.Finally) - throw new InvalidOperationException($"The opcode {OpCode.ENDTRY} can't be executed in a FINALLY block."); - - int endPointer = checked(CurrentContext.InstructionPointer + endOffset); - if (currentTry.HasFinally) - { - currentTry.State = ExceptionHandlingState.Finally; - currentTry.EndPointer = endPointer; - CurrentContext.InstructionPointer = currentTry.FinallyPointer; - } - else - { - CurrentContext.TryStack.Pop(); - CurrentContext.InstructionPointer = endPointer; - } - isJumping = true; - } - - /// - /// Jump to the specified position. - /// - /// The position to jump to. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected void ExecuteJump(int position) - { - if (position < 0 || position >= CurrentContext!.Script.Length) - throw new ArgumentOutOfRangeException($"Jump out of range for position: {position}"); - CurrentContext.InstructionPointer = position; - isJumping = true; - } - - /// - /// Jump to the specified offset from the current position. - /// - /// The offset from the current position to jump to. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ExecuteJumpOffset(int offset) - { - ExecuteJump(checked(CurrentContext!.InstructionPointer + offset)); - } - private void ExecuteLoadFromSlot(Slot? slot, int index) { if (slot is null) @@ -222,11 +146,11 @@ protected internal void ExecuteNext() PreExecuteInstruction(instruction); try { - JumpTable.GetMethod(instruction.OpCode)(this, instruction); + JumpTable[instruction.OpCode](this, instruction); } catch (CatchableException ex) when (Limits.CatchEngineExceptions) { - ExecuteThrow(ex.Message); + JumpTable.ExecuteThrow(this, ex.Message); } PostExecuteInstruction(instruction); if (!isJumping) context.MoveNext(); @@ -248,75 +172,11 @@ private void ExecuteStoreToSlot(Slot? slot, int index) slot[index] = Pop(); } - /// - /// Throws a specified exception in the VM. - /// - /// The exception to be thrown. - protected void ExecuteThrow(StackItem ex) - { - UncaughtException = ex; - HandleException(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void ExecuteTry(int catchOffset, int finallyOffset) - { - if (catchOffset == 0 && finallyOffset == 0) - throw new InvalidOperationException($"catchOffset and finallyOffset can't be 0 in a TRY block"); - if (CurrentContext!.TryStack is null) - CurrentContext.TryStack = new Stack(); - else if (CurrentContext.TryStack.Count >= Limits.MaxTryNestingDepth) - throw new InvalidOperationException("MaxTryNestingDepth exceed."); - int catchPointer = catchOffset == 0 ? -1 : checked(CurrentContext.InstructionPointer + catchOffset); - int finallyPointer = finallyOffset == 0 ? -1 : checked(CurrentContext.InstructionPointer + finallyOffset); - CurrentContext.TryStack.Push(new ExceptionHandlingContext(catchPointer, finallyPointer)); - } - - private void HandleException() - { - int pop = 0; - foreach (var executionContext in InvocationStack) - { - if (executionContext.TryStack != null) - { - while (executionContext.TryStack.TryPeek(out var tryContext)) - { - if (tryContext.State == ExceptionHandlingState.Finally || (tryContext.State == ExceptionHandlingState.Catch && !tryContext.HasFinally)) - { - executionContext.TryStack.Pop(); - continue; - } - for (int i = 0; i < pop; i++) - { - ContextUnloaded(InvocationStack.Pop()); - } - if (tryContext.State == ExceptionHandlingState.Try && tryContext.HasCatch) - { - tryContext.State = ExceptionHandlingState.Catch; - Push(UncaughtException!); - executionContext.InstructionPointer = tryContext.CatchPointer; - UncaughtException = null; - } - else - { - tryContext.State = ExceptionHandlingState.Finally; - executionContext.InstructionPointer = tryContext.FinallyPointer; - } - isJumping = true; - return; - } - } - ++pop; - } - - throw new VMUnhandledException(UncaughtException!); - } - /// /// Loads the specified context into the invocation stack. /// /// The context to load. - protected virtual void LoadContext(ExecutionContext context) + public virtual void LoadContext(ExecutionContext context) { if (InvocationStack.Count >= Limits.MaxInvocationStackSize) throw new InvalidOperationException($"MaxInvocationStackSize exceed: {InvocationStack.Count}"); @@ -325,6 +185,29 @@ protected virtual void LoadContext(ExecutionContext context) CurrentContext = context; } + /// + /// Called when a context is unloaded. + /// + /// The context being unloaded. + public virtual void UnloadedContext(ExecutionContext context) + { + if (InvocationStack.Count == 0) + { + CurrentContext = null; + EntryContext = null; + } + else + { + CurrentContext = InvocationStack.Peek(); + } + if (context.StaticFields != null && context.StaticFields != CurrentContext?.StaticFields) + { + context.StaticFields.ClearReferences(); + } + context.LocalVariables?.ClearReferences(); + context.Arguments?.ClearReferences(); + } + /// /// Create a new context with the specified script without loading. /// @@ -355,17 +238,6 @@ public ExecutionContext LoadScript(Script script, int rvcount = -1, int initialP return context; } - /// - /// When overridden in a derived class, loads the specified method token. - /// Called when is executed. - /// - /// The method token to be loaded. - /// The created context. - protected virtual ExecutionContext LoadToken(ushort token) - { - throw new InvalidOperationException($"Token not found: {token}"); - } - /// /// Called when an exception that cannot be caught by the VM is thrown. /// @@ -382,16 +254,6 @@ protected virtual void OnStateChanged() { } - /// - /// When overridden in a derived class, invokes the specified system call. - /// Called when is executed. - /// - /// The system call to be invoked. - protected virtual void OnSysCall(uint method) - { - throw new InvalidOperationException($"Syscall not found: {method}"); - } - /// /// Returns the item at the specified index from the top of the current stack without removing it. /// diff --git a/src/Neo.VM/JumpTable.Control.cs b/src/Neo.VM/JumpTable.Control.cs index 5d5d172c3c..7d1b9f9542 100644 --- a/src/Neo.VM/JumpTable.Control.cs +++ b/src/Neo.VM/JumpTable.Control.cs @@ -9,6 +9,9 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. +using Neo.VM.Types; +using System; +using System.Collections.Generic; using System.Runtime.CompilerServices; namespace Neo.VM @@ -23,41 +26,41 @@ public virtual void NOP(ExecutionEngine engine, Instruction instruction) [MethodImpl(MethodImplOptions.AggressiveInlining)] public virtual void JMP(ExecutionEngine engine, Instruction instruction) { - engine.ExecuteJumpOffset(instruction.TokenI8); + ExecuteJumpOffset(engine, instruction.TokenI8); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public virtual void JMP_L(ExecutionEngine engine, Instruction instruction) { - engine.ExecuteJumpOffset(instruction.TokenI32); + ExecuteJumpOffset(engine, instruction.TokenI32); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public virtual void JMPIF(ExecutionEngine engine, Instruction instruction) { if (engine.Pop().GetBoolean()) - engine.ExecuteJumpOffset(instruction.TokenI8); + ExecuteJumpOffset(engine, instruction.TokenI8); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public virtual void JMPIF_L(ExecutionEngine engine, Instruction instruction) { if (engine.Pop().GetBoolean()) - engine.ExecuteJumpOffset(instruction.TokenI32); + ExecuteJumpOffset(engine, instruction.TokenI32); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public virtual void JMPIFNOT(ExecutionEngine engine, Instruction instruction) { if (!engine.Pop().GetBoolean()) - engine.ExecuteJumpOffset(instruction.TokenI8); + ExecuteJumpOffset(engine, instruction.TokenI8); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public virtual void JMPIFNOT_L(ExecutionEngine engine, Instruction instruction) { if (!engine.Pop().GetBoolean()) - engine.ExecuteJumpOffset(instruction.TokenI32); + ExecuteJumpOffset(engine, instruction.TokenI32); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -66,7 +69,7 @@ public virtual void JMPEQ(ExecutionEngine engine, Instruction instruction) var x2 = engine.Pop().GetInteger(); var x1 = engine.Pop().GetInteger(); if (x1 == x2) - engine.ExecuteJumpOffset(instruction.TokenI8); + ExecuteJumpOffset(engine, instruction.TokenI8); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -75,7 +78,7 @@ public virtual void JMPEQ_L(ExecutionEngine engine, Instruction instruction) var x2 = engine.Pop().GetInteger(); var x1 = engine.Pop().GetInteger(); if (x1 == x2) - engine.ExecuteJumpOffset(instruction.TokenI32); + ExecuteJumpOffset(engine, instruction.TokenI32); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -84,7 +87,7 @@ public virtual void JMPNE(ExecutionEngine engine, Instruction instruction) var x2 = engine.Pop().GetInteger(); var x1 = engine.Pop().GetInteger(); if (x1 != x2) - engine.ExecuteJumpOffset(instruction.TokenI8); + ExecuteJumpOffset(engine, instruction.TokenI8); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -93,7 +96,7 @@ public virtual void JMPNE_L(ExecutionEngine engine, Instruction instruction) var x2 = engine.Pop().GetInteger(); var x1 = engine.Pop().GetInteger(); if (x1 != x2) - engine.ExecuteJumpOffset(instruction.TokenI32); + ExecuteJumpOffset(engine, instruction.TokenI32); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -102,7 +105,7 @@ public virtual void JMPGT(ExecutionEngine engine, Instruction instruction) var x2 = engine.Pop().GetInteger(); var x1 = engine.Pop().GetInteger(); if (x1 > x2) - engine.ExecuteJumpOffset(instruction.TokenI8); + ExecuteJumpOffset(engine, instruction.TokenI8); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -111,7 +114,7 @@ public virtual void JMPGT_L(ExecutionEngine engine, Instruction instruction) var x2 = engine.Pop().GetInteger(); var x1 = engine.Pop().GetInteger(); if (x1 > x2) - engine.ExecuteJumpOffset(instruction.TokenI32); + ExecuteJumpOffset(engine, instruction.TokenI32); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -120,7 +123,7 @@ public virtual void JMPGE(ExecutionEngine engine, Instruction instruction) var x2 = engine.Pop().GetInteger(); var x1 = engine.Pop().GetInteger(); if (x1 >= x2) - engine.ExecuteJumpOffset(instruction.TokenI8); + ExecuteJumpOffset(engine, instruction.TokenI8); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -129,7 +132,7 @@ public virtual void JMPGE_L(ExecutionEngine engine, Instruction instruction) var x2 = engine.Pop().GetInteger(); var x1 = engine.Pop().GetInteger(); if (x1 >= x2) - engine.ExecuteJumpOffset(instruction.TokenI32); + ExecuteJumpOffset(engine, instruction.TokenI32); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -138,7 +141,7 @@ public virtual void JMPLT(ExecutionEngine engine, Instruction instruction) var x2 = engine.Pop().GetInteger(); var x1 = engine.Pop().GetInteger(); if (x1 < x2) - engine.ExecuteJumpOffset(instruction.TokenI8); + ExecuteJumpOffset(engine, instruction.TokenI8); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -147,7 +150,7 @@ public virtual void JMPLT_L(ExecutionEngine engine, Instruction instruction) var x2 = engine.Pop().GetInteger(); var x1 = engine.Pop().GetInteger(); if (x1 < x2) - engine.ExecuteJumpOffset(instruction.TokenI32); + ExecuteJumpOffset(engine, instruction.TokenI32); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -156,7 +159,7 @@ public virtual void JMPLE(ExecutionEngine engine, Instruction instruction) var x2 = engine.Pop().GetInteger(); var x1 = engine.Pop().GetInteger(); if (x1 <= x2) - engine.ExecuteJumpOffset(instruction.TokenI8); + ExecuteJumpOffset(engine, instruction.TokenI8); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -165,34 +168,229 @@ public virtual void JMPLE_L(ExecutionEngine engine, Instruction instruction) var x2 = engine.Pop().GetInteger(); var x1 = engine.Pop().GetInteger(); if (x1 <= x2) - engine.ExecuteJumpOffset(instruction.TokenI32); + ExecuteJumpOffset(engine, instruction.TokenI32); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public virtual void CALL(ExecutionEngine engine, Instruction instruction) { - engine.ExecuteCall(checked(engine.CurrentContext!.InstructionPointer + instruction.TokenI8)); + ExecuteCall(engine, checked(engine.CurrentContext!.InstructionPointer + instruction.TokenI8)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public virtual void CALL_L(ExecutionEngine engine, Instruction instruction) { - engine.ExecuteCall(checked(engine.CurrentContext!.InstructionPointer + instruction.TokenI32)); - } - - /* TODO - case OpCode.CALLA: - case OpCode.CALLT: - case OpCode.ABORT: - case OpCode.ASSERT: - case OpCode.THROW: - case OpCode.TRY: - case OpCode.TRY_L: - case OpCode.ENDTRY: - case OpCode.ENDTRY_L: - case OpCode.ENDFINALLY: - case OpCode.RET: - case OpCode.SYSCALL: - */ + ExecuteCall(engine, checked(engine.CurrentContext!.InstructionPointer + instruction.TokenI32)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void CALLA(ExecutionEngine engine, Instruction instruction) + { + var x = engine.Pop(); + if (x.Script != engine.CurrentContext!.Script) + throw new InvalidOperationException("Pointers can't be shared between scripts"); + ExecuteCall(engine, x.Position); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void CALLT(ExecutionEngine engine, Instruction instruction) + { + throw new InvalidOperationException($"Token not found: {instruction.TokenU16}"); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void ABORT(ExecutionEngine engine, Instruction instruction) + { + throw new Exception($"{OpCode.ABORT} is executed."); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void ASSERT(ExecutionEngine engine, Instruction instruction) + { + var x = engine.Pop().GetBoolean(); + if (!x) + throw new Exception($"{OpCode.ASSERT} is executed with false result."); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void THROW(ExecutionEngine engine, Instruction instruction) + { + ExecuteThrow(engine, engine.Pop()); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void TRY(ExecutionEngine engine, Instruction instruction) + { + int catchOffset = instruction.TokenI8; + int finallyOffset = instruction.TokenI8_1; + ExecuteTry(engine, catchOffset, finallyOffset); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void TRY_L(ExecutionEngine engine, Instruction instruction) + { + int catchOffset = instruction.TokenI32; + int finallyOffset = instruction.TokenI32_1; + ExecuteTry(engine, catchOffset, finallyOffset); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void ENDTRY(ExecutionEngine engine, Instruction instruction) + { + int endOffset = instruction.TokenI8; + ExecuteEndTry(engine, endOffset); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void ENDTRY_L(ExecutionEngine engine, Instruction instruction) + { + int endOffset = instruction.TokenI32; + ExecuteEndTry(engine, endOffset); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void ENDFINALLY(ExecutionEngine engine, Instruction instruction) + { + if (engine.CurrentContext!.TryStack is null) + throw new InvalidOperationException($"The corresponding TRY block cannot be found."); + if (!engine.CurrentContext.TryStack.TryPop(out ExceptionHandlingContext? currentTry)) + throw new InvalidOperationException($"The corresponding TRY block cannot be found."); + + if (engine.UncaughtException is null) + engine.CurrentContext.InstructionPointer = currentTry.EndPointer; + else + ExecuteThrow(engine, engine.UncaughtException); + + engine.isJumping = true; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void RET(ExecutionEngine engine, Instruction instruction) + { + ExecutionContext context_pop = engine.InvocationStack.Pop(); + EvaluationStack stack_eval = engine.InvocationStack.Count == 0 ? engine.ResultStack : engine.InvocationStack.Peek().EvaluationStack; + if (context_pop.EvaluationStack != stack_eval) + { + if (context_pop.RVCount >= 0 && context_pop.EvaluationStack.Count != context_pop.RVCount) + throw new InvalidOperationException("RVCount doesn't match with EvaluationStack"); + context_pop.EvaluationStack.CopyTo(stack_eval); + } + if (engine.InvocationStack.Count == 0) + engine.State = VMState.HALT; + engine.UnloadedContext(context_pop); + engine.isJumping = true; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void SYSCALL(ExecutionEngine engine, Instruction instruction) + { + throw new InvalidOperationException($"Syscall not found: {instruction.TokenU32}"); + } + + #region Execute methods + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void ExecuteCall(ExecutionEngine engine, int position) + { + engine.LoadContext(engine.CurrentContext!.Clone(position)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void ExecuteEndTry(ExecutionEngine engine, int endOffset) + { + if (engine.CurrentContext!.TryStack is null) + throw new InvalidOperationException($"The corresponding TRY block cannot be found."); + if (!engine.CurrentContext.TryStack.TryPeek(out ExceptionHandlingContext? currentTry)) + throw new InvalidOperationException($"The corresponding TRY block cannot be found."); + if (currentTry.State == ExceptionHandlingState.Finally) + throw new InvalidOperationException($"The opcode {OpCode.ENDTRY} can't be executed in a FINALLY block."); + + int endPointer = checked(engine.CurrentContext.InstructionPointer + endOffset); + if (currentTry.HasFinally) + { + currentTry.State = ExceptionHandlingState.Finally; + currentTry.EndPointer = endPointer; + engine.CurrentContext.InstructionPointer = currentTry.FinallyPointer; + } + else + { + engine.CurrentContext.TryStack.Pop(); + engine.CurrentContext.InstructionPointer = endPointer; + } + engine.isJumping = true; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void ExecuteJump(ExecutionEngine engine, int position) + { + if (position < 0 || position >= engine.CurrentContext!.Script.Length) + throw new ArgumentOutOfRangeException($"Jump out of range for position: {position}"); + engine.CurrentContext.InstructionPointer = position; + engine.isJumping = true; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void ExecuteJumpOffset(ExecutionEngine engine, int offset) + { + ExecuteJump(engine, checked(engine.CurrentContext!.InstructionPointer + offset)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void ExecuteTry(ExecutionEngine engine, int catchOffset, int finallyOffset) + { + if (catchOffset == 0 && finallyOffset == 0) + throw new InvalidOperationException($"catchOffset and finallyOffset can't be 0 in a TRY block"); + if (engine.CurrentContext!.TryStack is null) + engine.CurrentContext.TryStack = new Stack(); + else if (engine.CurrentContext.TryStack.Count >= engine.Limits.MaxTryNestingDepth) + throw new InvalidOperationException("MaxTryNestingDepth exceed."); + int catchPointer = catchOffset == 0 ? -1 : checked(engine.CurrentContext.InstructionPointer + catchOffset); + int finallyPointer = finallyOffset == 0 ? -1 : checked(engine.CurrentContext.InstructionPointer + finallyOffset); + engine.CurrentContext.TryStack.Push(new ExceptionHandlingContext(catchPointer, finallyPointer)); + } + + public void ExecuteThrow(ExecutionEngine engine, StackItem? ex) + { + engine.UncaughtException = ex; + + int pop = 0; + foreach (var executionContext in engine.InvocationStack) + { + if (executionContext.TryStack != null) + { + while (executionContext.TryStack.TryPeek(out var tryContext)) + { + if (tryContext.State == ExceptionHandlingState.Finally || (tryContext.State == ExceptionHandlingState.Catch && !tryContext.HasFinally)) + { + executionContext.TryStack.Pop(); + continue; + } + for (int i = 0; i < pop; i++) + { + engine.UnloadedContext(engine.InvocationStack.Pop()); + } + if (tryContext.State == ExceptionHandlingState.Try && tryContext.HasCatch) + { + tryContext.State = ExceptionHandlingState.Catch; + engine.Push(engine.UncaughtException!); + executionContext.InstructionPointer = tryContext.CatchPointer; + engine.UncaughtException = null; + } + else + { + tryContext.State = ExceptionHandlingState.Finally; + executionContext.InstructionPointer = tryContext.FinallyPointer; + } + engine.isJumping = true; + return; + } + } + ++pop; + } + + throw new VMUnhandledException(engine.UncaughtException!); + } + + #endregion } } diff --git a/src/Neo.VM/JumpTable.cs b/src/Neo.VM/JumpTable.cs index db68fe71c8..e78ce5d606 100644 --- a/src/Neo.VM/JumpTable.cs +++ b/src/Neo.VM/JumpTable.cs @@ -18,14 +18,16 @@ public partial class JumpTable public delegate void DelAction(ExecutionEngine engine, Instruction instruction); protected readonly DelAction[] _table = new DelAction[byte.MaxValue]; - /// - /// Get Method - /// - /// OpCode - /// Action - public DelAction GetMethod(OpCode opCode) + public DelAction this[OpCode opCode] { - return _table[(byte)opCode]; + get + { + return _table[(byte)opCode]; + } + set + { + _table[(byte)opCode] = value; + } } public JumpTable() diff --git a/src/Neo/SmartContract/ApplicationEngine.cs b/src/Neo/SmartContract/ApplicationEngine.cs index ba161e6951..02b7691f87 100644 --- a/src/Neo/SmartContract/ApplicationEngine.cs +++ b/src/Neo/SmartContract/ApplicationEngine.cs @@ -9,6 +9,7 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. +using Akka.Configuration.Hocon; using Neo.IO; using Neo.Json; using Neo.Network.P2P.Payloads; @@ -32,6 +33,8 @@ namespace Neo.SmartContract /// public partial class ApplicationEngine : ExecutionEngine { + private static readonly JumpTable JumpTable = ComposeJumpTable(); + /// /// The maximum cost that can be spent when a contract is executed in test mode. /// @@ -177,6 +180,58 @@ protected unsafe ApplicationEngine(TriggerType trigger, IVerifiable container, D diagnostic?.Initialized(this); } + #region JumpTable + + private static JumpTable ComposeJumpTable() + { + var table = new JumpTable(); + + table[OpCode.SYSCALL] = OnSysCall; + table[OpCode.CALLT] = OnCallT; + + return table; + } + + private static void OnCallT(ExecutionEngine engine, Instruction instruction) + { + if (engine is ApplicationEngine app) + { + uint tokenId = instruction.TokenU16; + + app.ValidateCallFlags(CallFlags.ReadStates | CallFlags.AllowCall); + ContractState contract = app.CurrentContext.GetState().Contract; + if (contract is null || tokenId >= contract.Nef.Tokens.Length) + throw new InvalidOperationException(); + MethodToken token = contract.Nef.Tokens[tokenId]; + if (token.ParametersCount > app.CurrentContext.EvaluationStack.Count) + throw new InvalidOperationException(); + StackItem[] args = new StackItem[token.ParametersCount]; + for (int i = 0; i < token.ParametersCount; i++) + args[i] = app.Pop(); + app.CallContractInternal(token.Hash, token.Method, token.CallFlags, token.HasReturnValue, args); + } + else + { + throw new InvalidOperationException(); + } + } + + private static void OnSysCall(ExecutionEngine engine, Instruction instruction) + { + if (engine is ApplicationEngine app) + { + uint method = instruction.TokenU16; + + app.OnSysCall(services[method]); + } + else + { + throw new InvalidOperationException(); + } + } + + #endregion + /// /// Adds GAS to and checks if it has exceeded the maximum limit. /// @@ -270,9 +325,9 @@ internal ContractTask CallFromNativeContract(UInt160 callingScriptHash, UI return task; } - protected override void ContextUnloaded(ExecutionContext context) + public override void UnloadedContext(ExecutionContext context) { - base.ContextUnloaded(context); + base.UnloadedContext(context); if (context.Script != CurrentContext?.Script) { ExecutionContextState state = context.GetState(); @@ -324,7 +379,7 @@ public static ApplicationEngine Create(TriggerType trigger, IVerifiable containe ?? new ApplicationEngine(trigger, container, snapshot, persistingBlock, settings, gas, diagnostic); } - protected override void LoadContext(ExecutionContext context) + public override void LoadContext(ExecutionContext context) { // Set default execution context state var state = context.GetState(); @@ -391,21 +446,6 @@ public ExecutionContext LoadScript(Script script, int rvcount = -1, int initialP return context; } - protected override ExecutionContext LoadToken(ushort tokenId) - { - ValidateCallFlags(CallFlags.ReadStates | CallFlags.AllowCall); - ContractState contract = CurrentContext.GetState().Contract; - if (contract is null || tokenId >= contract.Nef.Tokens.Length) - throw new InvalidOperationException(); - MethodToken token = contract.Nef.Tokens[tokenId]; - if (token.ParametersCount > CurrentContext.EvaluationStack.Count) - throw new InvalidOperationException(); - StackItem[] args = new StackItem[token.ParametersCount]; - for (int i = 0; i < token.ParametersCount; i++) - args[i] = Pop(); - return CallContractInternal(token.Hash, token.Method, token.CallFlags, token.HasReturnValue, args); - } - /// /// Converts an to a that used in the virtual machine. /// @@ -503,11 +543,6 @@ internal protected void ValidateCallFlags(CallFlags requiredCallFlags) throw new InvalidOperationException($"Cannot call this SYSCALL with the flag {state.CallFlags}."); } - protected override void OnSysCall(uint method) - { - OnSysCall(services[method]); - } - /// /// Invokes the specified interoperable service. /// diff --git a/tests/Neo.VM.Tests/Types/TestEngine.cs b/tests/Neo.VM.Tests/Types/TestEngine.cs index 07aa89a09f..cf99314892 100644 --- a/tests/Neo.VM.Tests/Types/TestEngine.cs +++ b/tests/Neo.VM.Tests/Types/TestEngine.cs @@ -19,21 +19,32 @@ class TestEngine : ExecutionEngine { public Exception FaultException { get; private set; } - protected override void OnSysCall(uint method) + public TestEngine() : base(ComposeJumpTable()) { } + + private static JumpTable ComposeJumpTable() { + JumpTable jumpTable = new JumpTable(); + jumpTable[OpCode.SYSCALL] = OnSysCall; + return jumpTable; + } + + private static void OnSysCall(ExecutionEngine engine, Instruction instruction) + { + uint method = instruction.TokenU32; + if (method == 0x77777777) { - CurrentContext.EvaluationStack.Push(StackItem.FromInterface(new object())); + engine.CurrentContext.EvaluationStack.Push(StackItem.FromInterface(new object())); return; } if (method == 0xaddeadde) { - ExecuteThrow("error"); + engine.JumpTable.ExecuteThrow(engine, "error"); return; } - throw new System.Exception(); + throw new Exception(); } protected override void OnFault(Exception ex)