Skip to content
This repository has been archived by the owner on Nov 22, 2023. It is now read-only.

Implement function pointers #253

Merged
merged 4 commits into from
Dec 6, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion src/neo-vm/ExecutionEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,13 @@ private bool ExecuteInstruction()
Push(new BigInteger(instruction.Operand.Span));
break;
}
case OpCode.PUSHA:
{
int position = instruction.TokenI32;
if (position < 0 || position > CurrentContext.Script.Length) return false;
Push(new Pointer(position));
break;
}
case OpCode.PUSHNULL:
{
Push(StackItem.Null);
Expand Down Expand Up @@ -197,9 +204,20 @@ private bool ExecuteInstruction()
return true;
}
case OpCode.CALL:
case OpCode.CALLA:
{
int position;
if (instruction.OpCode == OpCode.CALLA)
igormcoelho marked this conversation as resolved.
Show resolved Hide resolved
{
if (!TryPop(out Pointer x)) return false;
position = x.Position;
}
else
{
position = context.InstructionPointer + instruction.TokenI16;
}
ExecutionContext context_call = context.Clone();
context_call.InstructionPointer = context.InstructionPointer + instruction.TokenI16;
context_call.InstructionPointer = position;
if (context_call.InstructionPointer < 0 || context_call.InstructionPointer > context_call.Script.Length) return false;
LoadContext(context_call);
break;
Expand Down
14 changes: 12 additions & 2 deletions src/neo-vm/Instruction.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Buffers.Binary;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
Expand Down Expand Up @@ -34,7 +35,16 @@ public short TokenI16
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
return BitConverter.ToInt16(Operand.Span);
return BinaryPrimitives.ReadInt16LittleEndian(Operand.Span);
}
}

public int TokenI32
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
return BinaryPrimitives.ReadInt32LittleEndian(Operand.Span);
}
}

Expand All @@ -52,7 +62,7 @@ public uint TokenU32
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
return BitConverter.ToUInt32(Operand.Span);
return BinaryPrimitives.ReadUInt32LittleEndian(Operand.Span);
}
}

Expand Down
10 changes: 10 additions & 0 deletions src/neo-vm/OpCode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ public enum OpCode : byte
[OperandSize(Size = 32)]
PUSHINT256 = 0x05,
/// <summary>
/// Convert the next four bytes to an address, and push the address onto the stack.
/// </summary>
[OperandSize(Size = 4)]
erikzhang marked this conversation as resolved.
Show resolved Hide resolved
PUSHA = 0x0A,
/// <summary>
/// The item null is pushed onto the stack.
/// </summary>
PUSHNULL = 0x0B,
Expand Down Expand Up @@ -109,6 +114,11 @@ public enum OpCode : byte
PUSH16 = 0x20,

// Flow control

/// <summary>
/// Pop the address of a function from the stack, and call the function.
/// </summary>
CALLA = 0x3A,
/// <summary>
/// Does nothing.
/// </summary>
Expand Down
32 changes: 32 additions & 0 deletions src/neo-vm/Types/Pointer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using System.Diagnostics;

namespace Neo.VM.Types
{
[DebuggerDisplay("Type={GetType().Name}, Position={Position}")]
public class Pointer : StackItem
{
public int Position { get; }

public Pointer(int position)
{
this.Position = position;
}

public override bool Equals(StackItem other)
{
if (other == this) return true;
if (other is Pointer p) return Position == p.Position;
return false;
}

public override int GetHashCode()
{
return Position.GetHashCode();
}

public override bool ToBoolean()
{
return true;
}
shargon marked this conversation as resolved.
Show resolved Hide resolved
}
}
148 changes: 148 additions & 0 deletions tests/neo-vm.Tests/Tests/OpCodes/Control/CALLA.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
{
"category": "Control",
"name": "CALLA",
"tests": [
{
"name": "Wrong type",
"script": "0x123A",
"steps": [
{
"actions": [
"StepInto"
],
"result": {
"state": "Break",
"invocationStack": [
{
"instructionPointer": 1,
"nextInstruction": "CALLA",
"evaluationStack": [
{
"type": "Integer",
"value": 2
}
]
}
]
}
},
{
"actions": [
"StepInto"
],
"result": {
"state": "FAULT"
}
}
]
},
{
"name": "Real test",
"script": "0x0A070000003A661066",
igormcoelho marked this conversation as resolved.
Show resolved Hide resolved
"steps": [
{
"actions": [
"StepInto"
],
"result": {
"state": "Break",
"invocationStack": [
{
"instructionPointer": 5,
"nextInstruction": "CALLA",
"evaluationStack": [
{
"type": "Pointer",
"value": 7
}
]
}
]
}
},
{
"actions": [
"StepInto"
],
"result": {
"state": "Break",
"invocationStack": [
{
"instructionPointer": 7,
"nextInstruction": "PUSH0"
},
{
"instructionPointer": 6,
"nextInstruction": "RET"
}
]
}
},
{
"actions": [
"StepInto"
],
"result": {
"state": "Break",
"invocationStack": [
{
"instructionPointer": 8,
"nextInstruction": "RET",
"evaluationStack": [
{
"type": "Integer",
"value": 0
}
]
},
{
"instructionPointer": 6,
"nextInstruction": "RET",
"evaluationStack": [
{
"type": "Integer",
"value": 0
}
]
}
]
}
},
{
"actions": [
"StepInto"
],
"result": {
"state": "Break",
"invocationStack": [
{
"instructionPointer": 6,
"nextInstruction": "RET",
"evaluationStack": [
{
"type": "Integer",
"value": 0
}
]
}
]
}
},
{
"actions": [
"StepInto"
],
"result": {
"state": "Halt",
"resultStack": [
{
"type": "Integer",
"value": 0
}
]
}
}
]
}
]
}
74 changes: 74 additions & 0 deletions tests/neo-vm.Tests/Tests/OpCodes/Push/PUSHA.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
{
"category": "Push",
"name": "PUSHA",
"tests": [
{
"name": "Out of range [-1]",
"script": "0Affffffff",
erikzhang marked this conversation as resolved.
Show resolved Hide resolved
"steps": [
{
"actions": [
"Execute"
],
"result": {
"state": "Fault"
}
}
]
},
{
"name": "Out of range [>length]",
"script": "0Affffff7f",
"steps": [
{
"actions": [
"Execute"
],
"result": {
"state": "Fault"
}
}
]
},
{
"name": "Real test",
"script": "0A00000000",
"steps": [
{
"actions": [
"Execute"
],
"result": {
"state": "Halt",
"resultStack": [
{
"type": "Pointer",
"value": 0
}
]
}
}
]
},
{
"name": "Real test [=length]",
"script": "0A05000000",
"steps": [
{
"actions": [
"Execute"
],
"result": {
"state": "Halt",
"resultStack": [
{
"type": "Pointer",
"value": 5
}
]
}
}
]
}
]
}
5 changes: 5 additions & 0 deletions tests/neo-vm.Tests/Types/VMUTStackItemType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ public enum VMUTStackItemType
/// </summary>
Null,

/// <summary>
/// An address of function
/// </summary>
Pointer,

/// <summary>
/// Boolean (true,false)
/// </summary>
Expand Down
14 changes: 14 additions & 0 deletions tests/neo-vm.Tests/VMJsonTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,12 @@ private JObject PrepareJsonItem(VMUTStackItem item)
ret.Remove("value");
break;
}
case VMUTStackItemType.Pointer:
{
ret["type"] = VMUTStackItemType.Pointer.ToString();
ret["value"] = item.Value.Value<int>();
break;
}
case VMUTStackItemType.String:
{
// Easy access
Expand Down Expand Up @@ -194,6 +200,14 @@ private JToken ItemToJson(StackItem item)
["type"] = type,
};
}
case Pointer p:
{
return new JObject
{
["type"] = type,
["value"] = p.Position
};
}
case VM.Types.Boolean v: value = new JValue(v.ToBoolean()); break;
case VM.Types.Integer v: value = new JValue(v.ToBigInteger().ToString()); break;
case VM.Types.ByteArray v: value = new JValue(v.ToByteArray().ToArray()); break;
Expand Down