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

Commit

Permalink
Implement function pointers (#253)
Browse files Browse the repository at this point in the history
* Implement function pointers

* Add comments

* Add UT

* Add more UT
  • Loading branch information
erikzhang authored and vncoelho committed Dec 6, 2019
1 parent dcb9de9 commit dd9ddeb
Show file tree
Hide file tree
Showing 8 changed files with 314 additions and 3 deletions.
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)
{
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)]
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;
}
}
}
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",
"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",
"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

0 comments on commit dd9ddeb

Please sign in to comment.