From 79aab106944e9cda617486bebf9abbfff35535d5 Mon Sep 17 00:00:00 2001 From: Shargon Date: Thu, 16 May 2019 23:41:43 +0200 Subject: [PATCH] Jump table draft --- src/neo-vm/ExecutionEngine.cs | 1603 ++++++++++++++++----------------- src/neo-vm/InstructionSet.cs | 154 ++++ 2 files changed, 916 insertions(+), 841 deletions(-) create mode 100644 src/neo-vm/InstructionSet.cs diff --git a/src/neo-vm/ExecutionEngine.cs b/src/neo-vm/ExecutionEngine.cs index d2a6d4aa..3a7289e3 100644 --- a/src/neo-vm/ExecutionEngine.cs +++ b/src/neo-vm/ExecutionEngine.cs @@ -51,7 +51,7 @@ public class ExecutionEngine : IDisposable #endregion - private static readonly byte[] EmptyBytes = new byte[0]; + private InstructionSet.delOnInstruction[] _jumpTable; private int stackitem_count = 0; private bool is_stackitem_count_strict = true; @@ -111,7 +111,7 @@ public class ExecutionEngine : IDisposable /// Stack item count /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - private bool CheckStackSize(bool strict, int count = 1) + internal bool CheckStackSize(bool strict, int count = 1) { is_stackitem_count_strict &= strict; stackitem_count += count; @@ -170,6 +170,15 @@ private static int GetItemCount(IEnumerable items) #endregion + /// + /// Constructor + /// + /// Set + public ExecutionEngine(InstructionSet set = null) + { + _jumpTable = set == null ? InstructionSet.Default.GetJumpTable() : set.GetJumpTable(); + } + public virtual void Dispose() { InvocationStack.Clear(); @@ -208,886 +217,798 @@ private bool ExecuteInstruction() { ExecutionContext context = CurrentContext; Instruction instruction = context.CurrentInstruction; - if (instruction.OpCode >= OpCode.PUSHBYTES1 && instruction.OpCode <= OpCode.PUSHDATA4) + + var code = _jumpTable[(byte)instruction.OpCode]; + + if (code != null) { - if (!CheckMaxItemSize(instruction.Operand.Length)) return false; - context.EvaluationStack.Push(instruction.Operand); - if (!CheckStackSize(true)) return false; + return code(this, context, instruction); } - else switch (instruction.OpCode) - { - // Push value - case OpCode.PUSH0: - { - context.EvaluationStack.Push(EmptyBytes); - if (!CheckStackSize(true)) return false; - break; - } - case OpCode.PUSHM1: - 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: - { - context.EvaluationStack.Push((int)instruction.OpCode - (int)OpCode.PUSH1 + 1); - if (!CheckStackSize(true)) return false; - break; - } - // Control - case OpCode.NOP: break; - case OpCode.JMP: - case OpCode.JMPIF: - case OpCode.JMPIFNOT: - { - int offset = context.InstructionPointer + instruction.TokenI16; - if (offset < 0 || offset > context.Script.Length) return false; - bool fValue = true; - if (instruction.OpCode > OpCode.JMP) - { - CheckStackSize(false, -1); - fValue = context.EvaluationStack.Pop().GetBoolean(); + switch (instruction.OpCode) + { + // Stack ops + case OpCode.DUPFROMALTSTACKBOTTOM: + { + var item = context.AltStack.Peek(context.AltStack.Count - 1); + context.EvaluationStack.Push(item); + if (!CheckStackSize(true)) return false; + break; + } + case OpCode.DUPFROMALTSTACK: + { + context.EvaluationStack.Push(context.AltStack.Peek()); + if (!CheckStackSize(true)) return false; + break; + } - if (instruction.OpCode == OpCode.JMPIFNOT) - fValue = !fValue; - } - if (fValue) - context.InstructionPointer = offset; - else - context.InstructionPointer += 3; - return true; - } - case OpCode.CALL: - { - ExecutionContext context_call = context.Clone(); - context_call.InstructionPointer = context.InstructionPointer + instruction.TokenI16; - if (context_call.InstructionPointer < 0 || context_call.InstructionPointer > context_call.Script.Length) return false; - LoadContext(context_call); - break; - } - case OpCode.RET: - { - ExecutionContext context_pop = InvocationStack.Pop(); - int rvcount = context_pop.RVCount; - if (rvcount == -1) rvcount = context_pop.EvaluationStack.Count; - if (rvcount > 0) - { - if (context_pop.EvaluationStack.Count < rvcount) return false; - RandomAccessStack stack_eval; - if (InvocationStack.Count == 0) - stack_eval = ResultStack; - else - stack_eval = CurrentContext.EvaluationStack; - context_pop.EvaluationStack.CopyTo(stack_eval, rvcount); - } - if (context_pop.RVCount == -1 && InvocationStack.Count > 0) - { - context_pop.AltStack.CopyTo(CurrentContext.AltStack); - } - CheckStackSize(false, 0); - ContextUnloaded?.Invoke(this, context_pop); - if (InvocationStack.Count == 0) - { - State = VMState.HALT; - } - return true; - } - case OpCode.SYSCALL: - { - if (!OnSysCall(instruction.TokenU32) || !CheckStackSize(false, int.MaxValue)) - return false; - break; - } - // Stack ops - case OpCode.DUPFROMALTSTACKBOTTOM: - { - var item = context.AltStack.Peek(context.AltStack.Count - 1); - context.EvaluationStack.Push(item); - if (!CheckStackSize(true)) return false; - break; - } - case OpCode.DUPFROMALTSTACK: - { - context.EvaluationStack.Push(context.AltStack.Peek()); - if (!CheckStackSize(true)) return false; - break; - } - case OpCode.TOALTSTACK: - { - context.AltStack.Push(context.EvaluationStack.Pop()); - break; - } - case OpCode.FROMALTSTACK: - { - context.EvaluationStack.Push(context.AltStack.Pop()); - break; - } - case OpCode.XDROP: - { - int n = (int)context.EvaluationStack.Pop().GetBigInteger(); - if (n < 0) return false; - context.EvaluationStack.Remove(n); - CheckStackSize(false, -2); - break; - } - case OpCode.XSWAP: - { - int n = (int)context.EvaluationStack.Pop().GetBigInteger(); - if (n < 0) return false; - CheckStackSize(true, -1); - if (n == 0) break; - StackItem xn = context.EvaluationStack.Peek(n); - context.EvaluationStack.Set(n, context.EvaluationStack.Peek()); - context.EvaluationStack.Set(0, xn); - break; - } - case OpCode.XTUCK: - { - int n = (int)context.EvaluationStack.Pop().GetBigInteger(); - if (n <= 0) return false; - context.EvaluationStack.Insert(n, context.EvaluationStack.Peek()); - break; - } - case OpCode.DEPTH: - { - context.EvaluationStack.Push(context.EvaluationStack.Count); - if (!CheckStackSize(true)) return false; - break; - } - case OpCode.DROP: - { - context.EvaluationStack.Pop(); - CheckStackSize(false, -1); - break; - } - case OpCode.DUP: - { - context.EvaluationStack.Push(context.EvaluationStack.Peek()); - if (!CheckStackSize(true)) return false; - break; - } - case OpCode.NIP: - { - context.EvaluationStack.Remove(1); - CheckStackSize(false, -1); - break; - } - case OpCode.OVER: - { - context.EvaluationStack.Push(context.EvaluationStack.Peek(1)); - if (!CheckStackSize(true)) return false; - break; - } - case OpCode.PICK: - { - int n = (int)context.EvaluationStack.Pop().GetBigInteger(); - if (n < 0) return false; - context.EvaluationStack.Push(context.EvaluationStack.Peek(n)); - break; - } - case OpCode.ROLL: - { - int n = (int)context.EvaluationStack.Pop().GetBigInteger(); - if (n < 0) return false; - CheckStackSize(true, -1); - if (n == 0) break; - context.EvaluationStack.Push(context.EvaluationStack.Remove(n)); - break; - } - case OpCode.ROT: - { - context.EvaluationStack.Push(context.EvaluationStack.Remove(2)); - break; - } - case OpCode.SWAP: - { - context.EvaluationStack.Push(context.EvaluationStack.Remove(1)); - break; - } - case OpCode.TUCK: - { - context.EvaluationStack.Insert(2, context.EvaluationStack.Peek()); - if (!CheckStackSize(true)) return false; - break; - } - case OpCode.CAT: - { - byte[] x2 = context.EvaluationStack.Pop().GetByteArray(); - byte[] x1 = context.EvaluationStack.Pop().GetByteArray(); - byte[] result; - if (x1.Length == 0) - { - result = x2; - } - else if (x2.Length == 0) - { - result = x1; - } - else - { - int length = x1.Length + x2.Length; - if (!CheckMaxItemSize(length)) return false; - byte[] buffer = new byte[length]; - Unsafe.MemoryCopy(x1, 0, buffer, 0, x1.Length); - Unsafe.MemoryCopy(x2, 0, buffer, x1.Length, x2.Length); - result = buffer; - } - context.EvaluationStack.Push(result); - CheckStackSize(true, -1); - break; - } - case OpCode.SUBSTR: - { - int count = (int)context.EvaluationStack.Pop().GetBigInteger(); - if (count < 0) return false; - int index = (int)context.EvaluationStack.Pop().GetBigInteger(); - if (index < 0) return false; - byte[] x = context.EvaluationStack.Pop().GetByteArray(); - if (index > x.Length) return false; - if (index + count > x.Length) count = x.Length - index; - byte[] buffer = new byte[count]; - Unsafe.MemoryCopy(x, index, buffer, 0, count); - context.EvaluationStack.Push(buffer); - CheckStackSize(true, -2); - break; - } - case OpCode.LEFT: - { - int count = (int)context.EvaluationStack.Pop().GetBigInteger(); - if (count < 0) return false; - byte[] x = context.EvaluationStack.Pop().GetByteArray(); - byte[] buffer; - if (count >= x.Length) - { - buffer = x; - } - else - { - buffer = new byte[count]; - Unsafe.MemoryCopy(x, 0, buffer, 0, count); - } - context.EvaluationStack.Push(buffer); - CheckStackSize(true, -1); - break; - } - case OpCode.RIGHT: - { - int count = (int)context.EvaluationStack.Pop().GetBigInteger(); - if (count < 0) return false; - byte[] x = context.EvaluationStack.Pop().GetByteArray(); - if (count > x.Length) return false; - byte[] buffer; - if (count == x.Length) - { - buffer = x; - } - else - { - buffer = new byte[count]; - Unsafe.MemoryCopy(x, x.Length - count, buffer, 0, count); - } - context.EvaluationStack.Push(buffer); - CheckStackSize(true, -1); - break; - } - case OpCode.SIZE: - { - StackItem x = context.EvaluationStack.Pop(); - context.EvaluationStack.Push(x.GetByteLength()); - break; - } + case OpCode.TOALTSTACK: + { + context.AltStack.Push(context.EvaluationStack.Pop()); + break; + } + case OpCode.FROMALTSTACK: + { + context.EvaluationStack.Push(context.AltStack.Pop()); + break; + } + case OpCode.XDROP: + { + int n = (int)context.EvaluationStack.Pop().GetBigInteger(); + if (n < 0) return false; + context.EvaluationStack.Remove(n); + CheckStackSize(false, -2); + break; + } + case OpCode.XSWAP: + { + int n = (int)context.EvaluationStack.Pop().GetBigInteger(); + if (n < 0) return false; + CheckStackSize(true, -1); + if (n == 0) break; + StackItem xn = context.EvaluationStack.Peek(n); + context.EvaluationStack.Set(n, context.EvaluationStack.Peek()); + context.EvaluationStack.Set(0, xn); + break; + } + case OpCode.XTUCK: + { + int n = (int)context.EvaluationStack.Pop().GetBigInteger(); + if (n <= 0) return false; + context.EvaluationStack.Insert(n, context.EvaluationStack.Peek()); + break; + } + case OpCode.DEPTH: + { + context.EvaluationStack.Push(context.EvaluationStack.Count); + if (!CheckStackSize(true)) return false; + break; + } + case OpCode.DROP: + { + context.EvaluationStack.Pop(); + CheckStackSize(false, -1); + break; + } + case OpCode.DUP: + { + context.EvaluationStack.Push(context.EvaluationStack.Peek()); + if (!CheckStackSize(true)) return false; + break; + } + case OpCode.NIP: + { + context.EvaluationStack.Remove(1); + CheckStackSize(false, -1); + break; + } + case OpCode.OVER: + { + context.EvaluationStack.Push(context.EvaluationStack.Peek(1)); + if (!CheckStackSize(true)) return false; + break; + } + case OpCode.PICK: + { + int n = (int)context.EvaluationStack.Pop().GetBigInteger(); + if (n < 0) return false; + context.EvaluationStack.Push(context.EvaluationStack.Peek(n)); + break; + } + case OpCode.ROLL: + { + int n = (int)context.EvaluationStack.Pop().GetBigInteger(); + if (n < 0) return false; + CheckStackSize(true, -1); + if (n == 0) break; + context.EvaluationStack.Push(context.EvaluationStack.Remove(n)); + break; + } + case OpCode.ROT: + { + context.EvaluationStack.Push(context.EvaluationStack.Remove(2)); + break; + } + case OpCode.SWAP: + { + context.EvaluationStack.Push(context.EvaluationStack.Remove(1)); + break; + } + case OpCode.TUCK: + { + context.EvaluationStack.Insert(2, context.EvaluationStack.Peek()); + if (!CheckStackSize(true)) return false; + break; + } + case OpCode.CAT: + { + byte[] x2 = context.EvaluationStack.Pop().GetByteArray(); + byte[] x1 = context.EvaluationStack.Pop().GetByteArray(); + byte[] result; + if (x1.Length == 0) + { + result = x2; + } + else if (x2.Length == 0) + { + result = x1; + } + else + { + int length = x1.Length + x2.Length; + if (!CheckMaxItemSize(length)) return false; + byte[] buffer = new byte[length]; + Unsafe.MemoryCopy(x1, 0, buffer, 0, x1.Length); + Unsafe.MemoryCopy(x2, 0, buffer, x1.Length, x2.Length); + result = buffer; + } + context.EvaluationStack.Push(result); + CheckStackSize(true, -1); + break; + } + case OpCode.SUBSTR: + { + int count = (int)context.EvaluationStack.Pop().GetBigInteger(); + if (count < 0) return false; + int index = (int)context.EvaluationStack.Pop().GetBigInteger(); + if (index < 0) return false; + byte[] x = context.EvaluationStack.Pop().GetByteArray(); + if (index > x.Length) return false; + if (index + count > x.Length) count = x.Length - index; + byte[] buffer = new byte[count]; + Unsafe.MemoryCopy(x, index, buffer, 0, count); + context.EvaluationStack.Push(buffer); + CheckStackSize(true, -2); + break; + } + case OpCode.LEFT: + { + int count = (int)context.EvaluationStack.Pop().GetBigInteger(); + if (count < 0) return false; + byte[] x = context.EvaluationStack.Pop().GetByteArray(); + byte[] buffer; + if (count >= x.Length) + { + buffer = x; + } + else + { + buffer = new byte[count]; + Unsafe.MemoryCopy(x, 0, buffer, 0, count); + } + context.EvaluationStack.Push(buffer); + CheckStackSize(true, -1); + break; + } + case OpCode.RIGHT: + { + int count = (int)context.EvaluationStack.Pop().GetBigInteger(); + if (count < 0) return false; + byte[] x = context.EvaluationStack.Pop().GetByteArray(); + if (count > x.Length) return false; + byte[] buffer; + if (count == x.Length) + { + buffer = x; + } + else + { + buffer = new byte[count]; + Unsafe.MemoryCopy(x, x.Length - count, buffer, 0, count); + } + context.EvaluationStack.Push(buffer); + CheckStackSize(true, -1); + break; + } + case OpCode.SIZE: + { + StackItem x = context.EvaluationStack.Pop(); + context.EvaluationStack.Push(x.GetByteLength()); + break; + } - // Bitwise logic - case OpCode.INVERT: - { - BigInteger x = context.EvaluationStack.Pop().GetBigInteger(); - if (!CheckBigInteger(x)) return false; - context.EvaluationStack.Push(~x); - break; - } - case OpCode.AND: - { - BigInteger x2 = context.EvaluationStack.Pop().GetBigInteger(); - if (!CheckBigInteger(x2)) return false; - BigInteger x1 = context.EvaluationStack.Pop().GetBigInteger(); - if (!CheckBigInteger(x1)) return false; - context.EvaluationStack.Push(x1 & x2); - CheckStackSize(true, -1); - break; - } - case OpCode.OR: - { - BigInteger x2 = context.EvaluationStack.Pop().GetBigInteger(); - if (!CheckBigInteger(x2)) return false; - BigInteger x1 = context.EvaluationStack.Pop().GetBigInteger(); - if (!CheckBigInteger(x1)) return false; - context.EvaluationStack.Push(x1 | x2); - CheckStackSize(true, -1); - break; - } - case OpCode.XOR: - { - BigInteger x2 = context.EvaluationStack.Pop().GetBigInteger(); - if (!CheckBigInteger(x2)) return false; - BigInteger x1 = context.EvaluationStack.Pop().GetBigInteger(); - if (!CheckBigInteger(x1)) return false; - context.EvaluationStack.Push(x1 ^ x2); - CheckStackSize(true, -1); - break; - } - case OpCode.EQUAL: - { - StackItem x2 = context.EvaluationStack.Pop(); - StackItem x1 = context.EvaluationStack.Pop(); - context.EvaluationStack.Push(x1.Equals(x2)); - CheckStackSize(false, -1); - break; - } + // Bitwise logic + case OpCode.INVERT: + { + BigInteger x = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x)) return false; + context.EvaluationStack.Push(~x); + break; + } + case OpCode.AND: + { + BigInteger x2 = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x2)) return false; + BigInteger x1 = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x1)) return false; + context.EvaluationStack.Push(x1 & x2); + CheckStackSize(true, -1); + break; + } + case OpCode.OR: + { + BigInteger x2 = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x2)) return false; + BigInteger x1 = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x1)) return false; + context.EvaluationStack.Push(x1 | x2); + CheckStackSize(true, -1); + break; + } + case OpCode.XOR: + { + BigInteger x2 = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x2)) return false; + BigInteger x1 = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x1)) return false; + context.EvaluationStack.Push(x1 ^ x2); + CheckStackSize(true, -1); + break; + } + case OpCode.EQUAL: + { + StackItem x2 = context.EvaluationStack.Pop(); + StackItem x1 = context.EvaluationStack.Pop(); + context.EvaluationStack.Push(x1.Equals(x2)); + CheckStackSize(false, -1); + break; + } - // Numeric - case OpCode.INC: - { - BigInteger x = context.EvaluationStack.Pop().GetBigInteger(); - if (!CheckBigInteger(x)) return false; - x += 1; - if (!CheckBigInteger(x)) return false; - context.EvaluationStack.Push(x); - break; - } - case OpCode.DEC: - { - BigInteger x = context.EvaluationStack.Pop().GetBigInteger(); - if (!CheckBigInteger(x)) return false; - x -= 1; - if (!CheckBigInteger(x)) return false; - context.EvaluationStack.Push(x); - break; - } - case OpCode.SIGN: - { - BigInteger x = context.EvaluationStack.Pop().GetBigInteger(); - if (!CheckBigInteger(x)) return false; - context.EvaluationStack.Push(x.Sign); - break; - } - case OpCode.NEGATE: - { - BigInteger x = context.EvaluationStack.Pop().GetBigInteger(); - if (!CheckBigInteger(x)) return false; - context.EvaluationStack.Push(-x); - break; - } - case OpCode.ABS: - { - BigInteger x = context.EvaluationStack.Pop().GetBigInteger(); - if (!CheckBigInteger(x)) return false; - context.EvaluationStack.Push(BigInteger.Abs(x)); - break; - } - case OpCode.NOT: + // Numeric + case OpCode.INC: + { + BigInteger x = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x)) return false; + x += 1; + if (!CheckBigInteger(x)) return false; + context.EvaluationStack.Push(x); + break; + } + case OpCode.DEC: + { + BigInteger x = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x)) return false; + x -= 1; + if (!CheckBigInteger(x)) return false; + context.EvaluationStack.Push(x); + break; + } + case OpCode.SIGN: + { + BigInteger x = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x)) return false; + context.EvaluationStack.Push(x.Sign); + break; + } + case OpCode.NEGATE: + { + BigInteger x = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x)) return false; + context.EvaluationStack.Push(-x); + break; + } + case OpCode.ABS: + { + BigInteger x = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x)) return false; + context.EvaluationStack.Push(BigInteger.Abs(x)); + break; + } + case OpCode.NOT: + { + bool x = context.EvaluationStack.Pop().GetBoolean(); + context.EvaluationStack.Push(!x); + CheckStackSize(false, 0); + break; + } + case OpCode.NZ: + { + BigInteger x = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x)) return false; + context.EvaluationStack.Push(!x.IsZero); + break; + } + case OpCode.ADD: + { + BigInteger x2 = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x2)) return false; + BigInteger x1 = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x1)) return false; + BigInteger result = x1 + x2; + if (!CheckBigInteger(result)) return false; + context.EvaluationStack.Push(result); + CheckStackSize(true, -1); + break; + } + case OpCode.SUB: + { + BigInteger x2 = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x2)) return false; + BigInteger x1 = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x1)) return false; + BigInteger result = x1 - x2; + if (!CheckBigInteger(result)) return false; + context.EvaluationStack.Push(result); + CheckStackSize(true, -1); + break; + } + case OpCode.MUL: + { + BigInteger x2 = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x2)) return false; + BigInteger x1 = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x1)) return false; + BigInteger result = x1 * x2; + if (!CheckBigInteger(result)) return false; + context.EvaluationStack.Push(result); + CheckStackSize(true, -1); + break; + } + case OpCode.DIV: + { + BigInteger x2 = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x2)) return false; + BigInteger x1 = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x1)) return false; + context.EvaluationStack.Push(x1 / x2); + CheckStackSize(true, -1); + break; + } + case OpCode.MOD: + { + BigInteger x2 = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x2)) return false; + BigInteger x1 = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x1)) return false; + context.EvaluationStack.Push(x1 % x2); + CheckStackSize(true, -1); + break; + } + case OpCode.SHL: + { + int shift = (int)context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckShift(shift)) return false; + BigInteger x = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x)) return false; + x <<= shift; + if (!CheckBigInteger(x)) return false; + context.EvaluationStack.Push(x); + CheckStackSize(true, -1); + break; + } + case OpCode.SHR: + { + int shift = (int)context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckShift(shift)) return false; + BigInteger x = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x)) return false; + x >>= shift; + if (!CheckBigInteger(x)) return false; + context.EvaluationStack.Push(x); + CheckStackSize(true, -1); + break; + } + case OpCode.BOOLAND: + { + bool x2 = context.EvaluationStack.Pop().GetBoolean(); + bool x1 = context.EvaluationStack.Pop().GetBoolean(); + context.EvaluationStack.Push(x1 && x2); + CheckStackSize(false, -1); + break; + } + case OpCode.BOOLOR: + { + bool x2 = context.EvaluationStack.Pop().GetBoolean(); + bool x1 = context.EvaluationStack.Pop().GetBoolean(); + context.EvaluationStack.Push(x1 || x2); + CheckStackSize(false, -1); + break; + } + case OpCode.NUMEQUAL: + { + BigInteger x2 = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x2)) return false; + BigInteger x1 = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x1)) return false; + context.EvaluationStack.Push(x1 == x2); + CheckStackSize(true, -1); + break; + } + case OpCode.NUMNOTEQUAL: + { + BigInteger x2 = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x2)) return false; + BigInteger x1 = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x1)) return false; + context.EvaluationStack.Push(x1 != x2); + CheckStackSize(true, -1); + break; + } + case OpCode.LT: + { + BigInteger x2 = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x2)) return false; + BigInteger x1 = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x1)) return false; + context.EvaluationStack.Push(x1 < x2); + CheckStackSize(true, -1); + break; + } + case OpCode.GT: + { + BigInteger x2 = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x2)) return false; + BigInteger x1 = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x1)) return false; + context.EvaluationStack.Push(x1 > x2); + CheckStackSize(true, -1); + break; + } + case OpCode.LTE: + { + BigInteger x2 = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x2)) return false; + BigInteger x1 = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x1)) return false; + context.EvaluationStack.Push(x1 <= x2); + CheckStackSize(true, -1); + break; + } + case OpCode.GTE: + { + BigInteger x2 = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x2)) return false; + BigInteger x1 = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x1)) return false; + context.EvaluationStack.Push(x1 >= x2); + CheckStackSize(true, -1); + break; + } + case OpCode.MIN: + { + BigInteger x2 = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x2)) return false; + BigInteger x1 = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x1)) return false; + context.EvaluationStack.Push(BigInteger.Min(x1, x2)); + CheckStackSize(true, -1); + break; + } + case OpCode.MAX: + { + BigInteger x2 = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x2)) return false; + BigInteger x1 = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x1)) return false; + context.EvaluationStack.Push(BigInteger.Max(x1, x2)); + CheckStackSize(true, -1); + break; + } + case OpCode.WITHIN: + { + BigInteger b = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(b)) return false; + BigInteger a = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(a)) return false; + BigInteger x = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x)) return false; + context.EvaluationStack.Push(a <= x && x < b); + CheckStackSize(true, -2); + break; + } + + // Crypto + case OpCode.SHA1: + using (SHA1 sha = SHA1.Create()) + { + byte[] x = context.EvaluationStack.Pop().GetByteArray(); + context.EvaluationStack.Push(sha.ComputeHash(x)); + break; + } + case OpCode.SHA256: + using (SHA256 sha = SHA256.Create()) + { + byte[] x = context.EvaluationStack.Pop().GetByteArray(); + context.EvaluationStack.Push(sha.ComputeHash(x)); + break; + } + + // Array + case OpCode.ARRAYSIZE: + { + StackItem item = context.EvaluationStack.Pop(); + if (item is ICollection collection) { - bool x = context.EvaluationStack.Pop().GetBoolean(); - context.EvaluationStack.Push(!x); + context.EvaluationStack.Push(collection.Count); CheckStackSize(false, 0); - break; - } - case OpCode.NZ: - { - BigInteger x = context.EvaluationStack.Pop().GetBigInteger(); - if (!CheckBigInteger(x)) return false; - context.EvaluationStack.Push(!x.IsZero); - break; - } - case OpCode.ADD: - { - BigInteger x2 = context.EvaluationStack.Pop().GetBigInteger(); - if (!CheckBigInteger(x2)) return false; - BigInteger x1 = context.EvaluationStack.Pop().GetBigInteger(); - if (!CheckBigInteger(x1)) return false; - BigInteger result = x1 + x2; - if (!CheckBigInteger(result)) return false; - context.EvaluationStack.Push(result); - CheckStackSize(true, -1); - break; - } - case OpCode.SUB: - { - BigInteger x2 = context.EvaluationStack.Pop().GetBigInteger(); - if (!CheckBigInteger(x2)) return false; - BigInteger x1 = context.EvaluationStack.Pop().GetBigInteger(); - if (!CheckBigInteger(x1)) return false; - BigInteger result = x1 - x2; - if (!CheckBigInteger(result)) return false; - context.EvaluationStack.Push(result); - CheckStackSize(true, -1); - break; - } - case OpCode.MUL: - { - BigInteger x2 = context.EvaluationStack.Pop().GetBigInteger(); - if (!CheckBigInteger(x2)) return false; - BigInteger x1 = context.EvaluationStack.Pop().GetBigInteger(); - if (!CheckBigInteger(x1)) return false; - BigInteger result = x1 * x2; - if (!CheckBigInteger(result)) return false; - context.EvaluationStack.Push(result); - CheckStackSize(true, -1); - break; - } - case OpCode.DIV: - { - BigInteger x2 = context.EvaluationStack.Pop().GetBigInteger(); - if (!CheckBigInteger(x2)) return false; - BigInteger x1 = context.EvaluationStack.Pop().GetBigInteger(); - if (!CheckBigInteger(x1)) return false; - context.EvaluationStack.Push(x1 / x2); - CheckStackSize(true, -1); - break; - } - case OpCode.MOD: - { - BigInteger x2 = context.EvaluationStack.Pop().GetBigInteger(); - if (!CheckBigInteger(x2)) return false; - BigInteger x1 = context.EvaluationStack.Pop().GetBigInteger(); - if (!CheckBigInteger(x1)) return false; - context.EvaluationStack.Push(x1 % x2); - CheckStackSize(true, -1); - break; - } - case OpCode.SHL: - { - int shift = (int)context.EvaluationStack.Pop().GetBigInteger(); - if (!CheckShift(shift)) return false; - BigInteger x = context.EvaluationStack.Pop().GetBigInteger(); - if (!CheckBigInteger(x)) return false; - x <<= shift; - if (!CheckBigInteger(x)) return false; - context.EvaluationStack.Push(x); - CheckStackSize(true, -1); - break; - } - case OpCode.SHR: - { - int shift = (int)context.EvaluationStack.Pop().GetBigInteger(); - if (!CheckShift(shift)) return false; - BigInteger x = context.EvaluationStack.Pop().GetBigInteger(); - if (!CheckBigInteger(x)) return false; - x >>= shift; - if (!CheckBigInteger(x)) return false; - context.EvaluationStack.Push(x); - CheckStackSize(true, -1); - break; - } - case OpCode.BOOLAND: - { - bool x2 = context.EvaluationStack.Pop().GetBoolean(); - bool x1 = context.EvaluationStack.Pop().GetBoolean(); - context.EvaluationStack.Push(x1 && x2); - CheckStackSize(false, -1); - break; - } - case OpCode.BOOLOR: - { - bool x2 = context.EvaluationStack.Pop().GetBoolean(); - bool x1 = context.EvaluationStack.Pop().GetBoolean(); - context.EvaluationStack.Push(x1 || x2); - CheckStackSize(false, -1); - break; } - case OpCode.NUMEQUAL: + else { - BigInteger x2 = context.EvaluationStack.Pop().GetBigInteger(); - if (!CheckBigInteger(x2)) return false; - BigInteger x1 = context.EvaluationStack.Pop().GetBigInteger(); - if (!CheckBigInteger(x1)) return false; - context.EvaluationStack.Push(x1 == x2); - CheckStackSize(true, -1); - break; - } - case OpCode.NUMNOTEQUAL: - { - BigInteger x2 = context.EvaluationStack.Pop().GetBigInteger(); - if (!CheckBigInteger(x2)) return false; - BigInteger x1 = context.EvaluationStack.Pop().GetBigInteger(); - if (!CheckBigInteger(x1)) return false; - context.EvaluationStack.Push(x1 != x2); - CheckStackSize(true, -1); - break; + context.EvaluationStack.Push(item.GetByteLength()); + CheckStackSize(true, 0); } - case OpCode.LT: - { - BigInteger x2 = context.EvaluationStack.Pop().GetBigInteger(); - if (!CheckBigInteger(x2)) return false; - BigInteger x1 = context.EvaluationStack.Pop().GetBigInteger(); - if (!CheckBigInteger(x1)) return false; - context.EvaluationStack.Push(x1 < x2); - CheckStackSize(true, -1); - break; - } - case OpCode.GT: - { - BigInteger x2 = context.EvaluationStack.Pop().GetBigInteger(); - if (!CheckBigInteger(x2)) return false; - BigInteger x1 = context.EvaluationStack.Pop().GetBigInteger(); - if (!CheckBigInteger(x1)) return false; - context.EvaluationStack.Push(x1 > x2); - CheckStackSize(true, -1); - break; - } - case OpCode.LTE: - { - BigInteger x2 = context.EvaluationStack.Pop().GetBigInteger(); - if (!CheckBigInteger(x2)) return false; - BigInteger x1 = context.EvaluationStack.Pop().GetBigInteger(); - if (!CheckBigInteger(x1)) return false; - context.EvaluationStack.Push(x1 <= x2); - CheckStackSize(true, -1); - break; - } - case OpCode.GTE: - { - BigInteger x2 = context.EvaluationStack.Pop().GetBigInteger(); - if (!CheckBigInteger(x2)) return false; - BigInteger x1 = context.EvaluationStack.Pop().GetBigInteger(); - if (!CheckBigInteger(x1)) return false; - context.EvaluationStack.Push(x1 >= x2); - CheckStackSize(true, -1); - break; - } - case OpCode.MIN: - { - BigInteger x2 = context.EvaluationStack.Pop().GetBigInteger(); - if (!CheckBigInteger(x2)) return false; - BigInteger x1 = context.EvaluationStack.Pop().GetBigInteger(); - if (!CheckBigInteger(x1)) return false; - context.EvaluationStack.Push(BigInteger.Min(x1, x2)); - CheckStackSize(true, -1); - break; - } - case OpCode.MAX: - { - BigInteger x2 = context.EvaluationStack.Pop().GetBigInteger(); - if (!CheckBigInteger(x2)) return false; - BigInteger x1 = context.EvaluationStack.Pop().GetBigInteger(); - if (!CheckBigInteger(x1)) return false; - context.EvaluationStack.Push(BigInteger.Max(x1, x2)); - CheckStackSize(true, -1); - break; + break; + } + case OpCode.PACK: + { + int size = (int)context.EvaluationStack.Pop().GetBigInteger(); + if (size < 0 || size > context.EvaluationStack.Count || !CheckArraySize(size)) + return false; + List items = new List(size); + for (int i = 0; i < size; i++) + items.Add(context.EvaluationStack.Pop()); + context.EvaluationStack.Push(items); + break; + } + case OpCode.UNPACK: + { + if (!(context.EvaluationStack.Pop() is VMArray array)) + return false; + for (int i = array.Count - 1; i >= 0; i--) + context.EvaluationStack.Push(array[i]); + context.EvaluationStack.Push(array.Count); + if (!CheckStackSize(false, array.Count)) return false; + break; + } + case OpCode.PICKITEM: + { + StackItem key = context.EvaluationStack.Pop(); + if (key is ICollection) return false; + StackItem item = context.EvaluationStack.Pop(); + switch (item) + { + case VMArray array: + { + int index = (int)key.GetBigInteger(); + if (index < 0 || index >= array.Count) return false; + context.EvaluationStack.Push(array[index]); + CheckStackSize(false, -1); + break; + } + case Map map: + { + if (!map.TryGetValue(key, out StackItem value)) return false; + context.EvaluationStack.Push(value); + CheckStackSize(false, -1); + break; + } + default: + { + byte[] byteArray = item.GetByteArray(); + int index = (int)key.GetBigInteger(); + if (index < 0 || index >= byteArray.Length) return false; + context.EvaluationStack.Push((int)byteArray[index]); + CheckStackSize(true, -1); + break; + } } - case OpCode.WITHIN: - { - BigInteger b = context.EvaluationStack.Pop().GetBigInteger(); - if (!CheckBigInteger(b)) return false; - BigInteger a = context.EvaluationStack.Pop().GetBigInteger(); - if (!CheckBigInteger(a)) return false; - BigInteger x = context.EvaluationStack.Pop().GetBigInteger(); - if (!CheckBigInteger(x)) return false; - context.EvaluationStack.Push(a <= x && x < b); - CheckStackSize(true, -2); - break; + break; + } + case OpCode.SETITEM: + { + StackItem value = context.EvaluationStack.Pop(); + if (value is Struct s) value = s.Clone(); + StackItem key = context.EvaluationStack.Pop(); + if (key is ICollection) return false; + switch (context.EvaluationStack.Pop()) + { + case VMArray array: + { + int index = (int)key.GetBigInteger(); + if (index < 0 || index >= array.Count) return false; + array[index] = value; + break; + } + case Map map: + { + if (!map.ContainsKey(key) && !CheckArraySize(map.Count + 1)) + return false; + map[key] = value; + break; + } + default: + return false; } - // Crypto - case OpCode.SHA1: - using (SHA1 sha = SHA1.Create()) - { - byte[] x = context.EvaluationStack.Pop().GetByteArray(); - context.EvaluationStack.Push(sha.ComputeHash(x)); - break; - } - case OpCode.SHA256: - using (SHA256 sha = SHA256.Create()) - { - byte[] x = context.EvaluationStack.Pop().GetByteArray(); - context.EvaluationStack.Push(sha.ComputeHash(x)); - break; - } + if (!CheckStackSize(false, int.MaxValue)) + return false; + + break; + } + case OpCode.NEWARRAY: + case OpCode.NEWSTRUCT: + { + var item = context.EvaluationStack.Pop(); - // Array - case OpCode.ARRAYSIZE: + if (item is VMArray array) { - StackItem item = context.EvaluationStack.Pop(); - if (item is ICollection collection) + // Allow to convert between array and struct + + VMArray result = null; + + if (array is Struct) { - context.EvaluationStack.Push(collection.Count); - CheckStackSize(false, 0); + if (instruction.OpCode == OpCode.NEWSTRUCT) + result = array; } else { - context.EvaluationStack.Push(item.GetByteLength()); - CheckStackSize(true, 0); - } - break; - } - case OpCode.PACK: - { - int size = (int)context.EvaluationStack.Pop().GetBigInteger(); - if (size < 0 || size > context.EvaluationStack.Count || !CheckArraySize(size)) - return false; - List items = new List(size); - for (int i = 0; i < size; i++) - items.Add(context.EvaluationStack.Pop()); - context.EvaluationStack.Push(items); - break; - } - case OpCode.UNPACK: - { - if (!(context.EvaluationStack.Pop() is VMArray array)) - return false; - for (int i = array.Count - 1; i >= 0; i--) - context.EvaluationStack.Push(array[i]); - context.EvaluationStack.Push(array.Count); - if (!CheckStackSize(false, array.Count)) return false; - break; - } - case OpCode.PICKITEM: - { - StackItem key = context.EvaluationStack.Pop(); - if (key is ICollection) return false; - StackItem item = context.EvaluationStack.Pop(); - switch (item) - { - case VMArray array: - { - int index = (int)key.GetBigInteger(); - if (index < 0 || index >= array.Count) return false; - context.EvaluationStack.Push(array[index]); - CheckStackSize(false, -1); - break; - } - case Map map: - { - if (!map.TryGetValue(key, out StackItem value)) return false; - context.EvaluationStack.Push(value); - CheckStackSize(false, -1); - break; - } - default: - { - byte[] byteArray = item.GetByteArray(); - int index = (int)key.GetBigInteger(); - if (index < 0 || index >= byteArray.Length) return false; - context.EvaluationStack.Push((int)byteArray[index]); - CheckStackSize(true, -1); - break; - } - } - break; - } - case OpCode.SETITEM: - { - StackItem value = context.EvaluationStack.Pop(); - if (value is Struct s) value = s.Clone(); - StackItem key = context.EvaluationStack.Pop(); - if (key is ICollection) return false; - switch (context.EvaluationStack.Pop()) - { - case VMArray array: - { - int index = (int)key.GetBigInteger(); - if (index < 0 || index >= array.Count) return false; - array[index] = value; - break; - } - case Map map: - { - if (!map.ContainsKey(key) && !CheckArraySize(map.Count + 1)) - return false; - map[key] = value; - break; - } - default: - return false; + if (instruction.OpCode == OpCode.NEWARRAY) + result = array; } - if (!CheckStackSize(false, int.MaxValue)) - return false; + if (result is null) + result = instruction.OpCode == OpCode.NEWARRAY + ? new VMArray(array) + : new Struct(array); - break; + context.EvaluationStack.Push(result); } - case OpCode.NEWARRAY: - case OpCode.NEWSTRUCT: + else { - var item = context.EvaluationStack.Pop(); - - if (item is VMArray array) - { - // Allow to convert between array and struct + int count = (int)item.GetBigInteger(); - VMArray result = null; + if (count < 0 || !CheckArraySize(count)) return false; - if (array is Struct) - { - if (instruction.OpCode == OpCode.NEWSTRUCT) - result = array; - } - else - { - if (instruction.OpCode == OpCode.NEWARRAY) - result = array; - } + List items = new List(count); - if (result is null) - result = instruction.OpCode == OpCode.NEWARRAY - ? new VMArray(array) - : new Struct(array); - - context.EvaluationStack.Push(result); - } - else + for (var i = 0; i < count; i++) { - int count = (int)item.GetBigInteger(); - - if (count < 0 || !CheckArraySize(count)) return false; - - List items = new List(count); - - for (var i = 0; i < count; i++) - { - items.Add(false); - } + items.Add(false); + } - VMArray result = instruction.OpCode == OpCode.NEWARRAY - ? new VMArray(items) - : new Struct(items); + VMArray result = instruction.OpCode == OpCode.NEWARRAY + ? new VMArray(items) + : new Struct(items); - context.EvaluationStack.Push(result); + context.EvaluationStack.Push(result); - if (!CheckStackSize(true, count)) return false; - } - break; - } - case OpCode.NEWMAP: - { - context.EvaluationStack.Push(new Map()); - if (!CheckStackSize(true)) return false; - break; - } - case OpCode.APPEND: - { - StackItem newItem = context.EvaluationStack.Pop(); - if (newItem is Struct s) newItem = s.Clone(); - StackItem arrItem = context.EvaluationStack.Pop(); - if (!(arrItem is VMArray array)) return false; - if (!CheckArraySize(array.Count + 1)) return false; - array.Add(newItem); - if (!CheckStackSize(false, int.MaxValue)) return false; - break; - } - case OpCode.REVERSE: - { - StackItem arrItem = context.EvaluationStack.Pop(); - CheckStackSize(false, -1); - if (!(arrItem is VMArray array)) return false; - array.Reverse(); - break; - } - case OpCode.REMOVE: - { - StackItem key = context.EvaluationStack.Pop(); - if (key is ICollection) return false; - StackItem value = context.EvaluationStack.Pop(); - CheckStackSize(false, -2); - switch (value) - { - case VMArray array: - int index = (int)key.GetBigInteger(); - if (index < 0 || index >= array.Count) return false; - array.RemoveAt(index); - break; - case Map map: - map.Remove(key); - break; - default: - return false; - } - break; - } - case OpCode.HASKEY: - { - StackItem key = context.EvaluationStack.Pop(); - if (key is ICollection) return false; - switch (context.EvaluationStack.Pop()) - { - case VMArray array: - int index = (int)key.GetBigInteger(); - if (index < 0) return false; - context.EvaluationStack.Push(index < array.Count); - break; - case Map map: - context.EvaluationStack.Push(map.ContainsKey(key)); - break; - default: - return false; - } - CheckStackSize(false, -1); - break; - } - case OpCode.KEYS: - { - if (!(context.EvaluationStack.Pop() is Map map)) return false; - context.EvaluationStack.Push(new VMArray(map.Keys)); - if (!CheckStackSize(false, map.Count)) return false; - break; - } - case OpCode.VALUES: - { - ICollection values; - switch (context.EvaluationStack.Pop()) - { - case VMArray array: - values = array; - break; - case Map map: - values = map.Values; - break; - default: - return false; - } - List newArray = new List(values.Count); - foreach (StackItem item in values) - if (item is Struct s) - newArray.Add(s.Clone()); - else - newArray.Add(item); - context.EvaluationStack.Push(new VMArray(newArray)); - if (!CheckStackSize(false, int.MaxValue)) return false; - break; + if (!CheckStackSize(true, count)) return false; + } + break; + } + case OpCode.NEWMAP: + { + context.EvaluationStack.Push(new Map()); + if (!CheckStackSize(true)) return false; + break; + } + case OpCode.APPEND: + { + StackItem newItem = context.EvaluationStack.Pop(); + if (newItem is Struct s) newItem = s.Clone(); + StackItem arrItem = context.EvaluationStack.Pop(); + if (!(arrItem is VMArray array)) return false; + if (!CheckArraySize(array.Count + 1)) return false; + array.Add(newItem); + if (!CheckStackSize(false, int.MaxValue)) return false; + break; + } + case OpCode.REVERSE: + { + StackItem arrItem = context.EvaluationStack.Pop(); + CheckStackSize(false, -1); + if (!(arrItem is VMArray array)) return false; + array.Reverse(); + break; + } + case OpCode.REMOVE: + { + StackItem key = context.EvaluationStack.Pop(); + if (key is ICollection) return false; + StackItem value = context.EvaluationStack.Pop(); + CheckStackSize(false, -2); + switch (value) + { + case VMArray array: + int index = (int)key.GetBigInteger(); + if (index < 0 || index >= array.Count) return false; + array.RemoveAt(index); + break; + case Map map: + map.Remove(key); + break; + default: + return false; } - - // Exceptions - case OpCode.THROW: - { - return false; + break; + } + case OpCode.HASKEY: + { + StackItem key = context.EvaluationStack.Pop(); + if (key is ICollection) return false; + switch (context.EvaluationStack.Pop()) + { + case VMArray array: + int index = (int)key.GetBigInteger(); + if (index < 0) return false; + context.EvaluationStack.Push(index < array.Count); + break; + case Map map: + context.EvaluationStack.Push(map.ContainsKey(key)); + break; + default: + return false; } - case OpCode.THROWIFNOT: - { - if (!context.EvaluationStack.Pop().GetBoolean()) + CheckStackSize(false, -1); + break; + } + case OpCode.KEYS: + { + if (!(context.EvaluationStack.Pop() is Map map)) return false; + context.EvaluationStack.Push(new VMArray(map.Keys)); + if (!CheckStackSize(false, map.Count)) return false; + break; + } + case OpCode.VALUES: + { + ICollection values; + switch (context.EvaluationStack.Pop()) + { + case VMArray array: + values = array; + break; + case Map map: + values = map.Values; + break; + default: return false; - CheckStackSize(false, -1); - break; } - default: + List newArray = new List(values.Count); + foreach (StackItem item in values) + if (item is Struct s) + newArray.Add(s.Clone()); + else + newArray.Add(item); + context.EvaluationStack.Push(new VMArray(newArray)); + if (!CheckStackSize(false, int.MaxValue)) return false; + break; + } + + // Exceptions + case OpCode.THROW: + { return false; - } + } + case OpCode.THROWIFNOT: + { + if (!context.EvaluationStack.Pop().GetBoolean()) + return false; + CheckStackSize(false, -1); + break; + } + default: + return false; + } context.MoveNext(); return true; } - private void LoadContext(ExecutionContext context) + internal void RaiseContextUnloaded(ExecutionContext e) => ContextUnloaded?.Invoke(this, e); + internal void RaiseContextLoaded(ExecutionContext e) => ContextLoaded?.Invoke(this, e); + + internal void LoadContext(ExecutionContext context) { if (InvocationStack.Count >= MaxInvocationStackSize) throw new InvalidOperationException(); @@ -1102,7 +1023,7 @@ public ExecutionContext LoadScript(byte[] script, int rvcount = -1) return context; } - protected virtual bool OnSysCall(uint method) => false; + internal virtual bool OnSysCall(uint method) => false; protected virtual bool PostExecuteInstruction(Instruction instruction) => true; diff --git a/src/neo-vm/InstructionSet.cs b/src/neo-vm/InstructionSet.cs new file mode 100644 index 00000000..8c0d1a3f --- /dev/null +++ b/src/neo-vm/InstructionSet.cs @@ -0,0 +1,154 @@ +namespace Neo.VM +{ + public class InstructionSet + { + /// + /// Default instruction set + /// + public static readonly InstructionSet Default = new InstructionSet(); + + private static readonly byte[] EmptyBytes = new byte[0]; + private delOnInstruction[] _jumpTable = null; + + public delegate bool delOnInstruction(ExecutionEngine engine, ExecutionContext context, Instruction instruction); + + /// + /// Get default jump table + /// + /// Return instruction array + public virtual delOnInstruction[] GetJumpTable() + { + if (_jumpTable != null) return _jumpTable; + + _jumpTable = new delOnInstruction[byte.MaxValue]; + + for (byte x = (byte)OpCode.PUSHBYTES1; x <= (byte)OpCode.PUSHDATA4; x++) + { + _jumpTable[x] = PUSHBYTES1_PUSHBYTES75; + } + + _jumpTable[(byte)OpCode.PUSH0] = PUSH0; + _jumpTable[(byte)OpCode.PUSHM1] = PUSHM1_PUSH16; + + for (byte x = (byte)OpCode.PUSH1; x <= (byte)OpCode.PUSH16; x++) + { + _jumpTable[x] = PUSHM1_PUSH16; + } + + for (byte x = (byte)OpCode.JMP; x <= (byte)OpCode.JMPIFNOT; x++) + { + _jumpTable[x] = JMP_JMPIF_JMPIFNOT; + } + + _jumpTable[(byte)OpCode.CALL] = CALL; + _jumpTable[(byte)OpCode.RET] = RET; + _jumpTable[(byte)OpCode.SYSCALL] = SYSCALL; + _jumpTable[(byte)OpCode.NOP] = (e, c, i) => { return true; }; + + return _jumpTable; + } + + public bool PUSH0(ExecutionEngine engine, ExecutionContext context, Instruction instruction) + { + context.EvaluationStack.Push(EmptyBytes); + if (!engine.CheckStackSize(true)) return false; + return true; + } + + public bool PUSHM1_PUSH16(ExecutionEngine engine, ExecutionContext context, Instruction instruction) + { + context.EvaluationStack.Push((int)instruction.OpCode - (int)OpCode.PUSH1 + 1); + if (!engine.CheckStackSize(true)) return false; + return true; + } + + public bool PUSHBYTES1_PUSHBYTES75(ExecutionEngine engine, ExecutionContext context, Instruction instruction) + { + if (!engine.CheckMaxItemSize(instruction.Operand.Length)) return false; + context.EvaluationStack.Push(instruction.Operand); + if (!engine.CheckStackSize(true)) return false; + return true; + } + + public bool JMP_JMPIF_JMPIFNOT(ExecutionEngine engine, ExecutionContext context, Instruction instruction) + { + int offset = context.InstructionPointer + instruction.TokenI16; + if (offset < 0 || offset > context.Script.Length) return false; + bool fValue = true; + if (instruction.OpCode > OpCode.JMP) + { + engine.CheckStackSize(false, -1); + fValue = context.EvaluationStack.Pop().GetBoolean(); + + if (instruction.OpCode == OpCode.JMPIFNOT) + fValue = !fValue; + } + if (fValue) + context.InstructionPointer = offset; + else + context.InstructionPointer += 3; + return true; + } + + public bool CALL(ExecutionEngine engine, ExecutionContext context, Instruction instruction) + { + ExecutionContext context_call = context.Clone(); + context_call.InstructionPointer = context.InstructionPointer + instruction.TokenI16; + if (context_call.InstructionPointer < 0 || context_call.InstructionPointer > context_call.Script.Length) return false; + + engine.LoadContext(context_call); + return true; + } + + public bool RET(ExecutionEngine engine, ExecutionContext context, Instruction instruction) + { + ExecutionContext context_pop = engine.InvocationStack.Pop(); + int rvcount = context_pop.RVCount; + if (rvcount == -1) rvcount = context_pop.EvaluationStack.Count; + if (rvcount > 0) + { + if (context_pop.EvaluationStack.Count < rvcount) return false; + RandomAccessStack stack_eval; + if (engine.InvocationStack.Count == 0) + stack_eval = engine.ResultStack; + else + stack_eval = engine.CurrentContext.EvaluationStack; + context_pop.EvaluationStack.CopyTo(stack_eval, rvcount); + } + if (context_pop.RVCount == -1 && engine.InvocationStack.Count > 0) + { + context_pop.AltStack.CopyTo(engine.CurrentContext.AltStack); + } + engine.CheckStackSize(false, 0); + engine.RaiseContextUnloaded(context_pop); + if (engine.InvocationStack.Count == 0) + { + engine.State = VMState.HALT; + } + return true; + } + + public bool SYSCALL(ExecutionEngine engine, ExecutionContext context, Instruction instruction) + { + if (!engine.OnSysCall(instruction.TokenU32) || !engine.CheckStackSize(false, int.MaxValue)) + return false; + + return true; + } + + public bool DUPFROMALTSTACKBOTTOM(ExecutionEngine engine, ExecutionContext context, Instruction instruction) + { + var item = context.AltStack.Peek(context.AltStack.Count - 1); + context.EvaluationStack.Push(item); + if (!engine.CheckStackSize(true)) return false; + return true; + } + + public bool DUPFROMALTSTACK(ExecutionEngine engine, ExecutionContext context, Instruction instruction) + { + context.EvaluationStack.Push(context.AltStack.Peek()); + if (!engine.CheckStackSize(true)) return false; + return true; + } + } +} \ No newline at end of file