Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ReferenceCounterV2 #3526

Open
wants to merge 23 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
22c2107
ReferenceCounterV2
shargon Oct 11, 2024
51ce4ba
update more interfaces
shargon Oct 11, 2024
a4b936b
Test DUP (count)
shargon Oct 11, 2024
01445c2
Merge branch 'master' into remove-tarjan-v2
shargon Oct 14, 2024
bc2e52e
Merge branch 'master' into remove-tarjan-v2
Jim8y Oct 24, 2024
93c42fc
Merge branch 'master' into remove-tarjan-v2
Jim8y Nov 1, 2024
cab91ca
Merge branch 'master' into remove-tarjan-v2
Jim8y Nov 3, 2024
71cc724
Merge branch 'master' into remove-tarjan-v2
Jim8y Nov 4, 2024
e158c5f
Check subitems
shargon Nov 4, 2024
37c1c00
Merge branch 'master' into remove-tarjan-v2
Jim8y Nov 7, 2024
c0efc7c
New version, not compatible with circular references
shargon Nov 7, 2024
6352c5b
Update tests/Neo.VM.Tests/UT_ReferenceCounter.cs
shargon Nov 7, 2024
8276180
Merge branch 'master' into remove-tarjan-v2
Jim8y Nov 8, 2024
21e5434
Merge branch 'master' into remove-tarjan-v2
shargon Nov 8, 2024
066cc8b
Some fixes, still with issues
shargon Nov 8, 2024
9347754
Merge branch 'remove-tarjan-v2' of https://github.com/neo-project/neo…
shargon Nov 8, 2024
57f122e
| Method | Mean | Error | StdDev |
shargon Nov 8, 2024
75ccbae
fix comment
shargon Nov 8, 2024
e2a10e5
Merge branch 'master' into remove-tarjan-v2
shargon Nov 8, 2024
0a85aad
update UT and engine
Jim8y Nov 9, 2024
e38f73e
Merge branch 'master' into remove-tarjan-v2
Jim8y Nov 9, 2024
a79dd96
Merge branch 'master' into remove-tarjan-v2
Jim8y Nov 9, 2024
4986c54
Merge branch 'master' into remove-tarjan-v2
cschuchardt88 Nov 12, 2024
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
2 changes: 1 addition & 1 deletion src/Neo.VM/ExecutionEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ protected internal set
/// <param name="jumpTable">The jump table to be used.</param>
/// <param name="referenceCounter">The reference counter to be used.</param>
/// <param name="limits">Restrictions on the VM.</param>
protected ExecutionEngine(JumpTable? jumpTable, IReferenceCounter referenceCounter, ExecutionEngineLimits limits)
public ExecutionEngine(JumpTable? jumpTable, IReferenceCounter referenceCounter, ExecutionEngineLimits limits)
{
JumpTable = jumpTable ?? JumpTable.Default;
Limits = limits;
Expand Down
67 changes: 67 additions & 0 deletions src/Neo.VM/ReferenceCounterV2.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// Copyright (C) 2015-2024 The Neo Project.
//
// ReferenceCounterV2.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;

namespace Neo.VM
{
/// <summary>
/// Used for reference counting of objects in the VM.
/// </summary>
public sealed class ReferenceCounterV2 : IReferenceCounter
{
/// <summary>
/// Gets the count of references.
/// </summary>
public int Count { get; private set; }

/// <summary>
/// Adds item to Reference Counter
/// </summary>
/// <param name="item">The item to add.</param>
/// <param name="count">Number of similar entries</param>
public void AddStackReference(StackItem item, int count = 1)
{
Count += count;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}

/// <summary>
/// Removes item from Reference Counter
/// </summary>
/// <param name="item">The item to remove.</param>
public void RemoveStackReference(StackItem item)
{
if (Count == 0)
{
throw new InvalidOperationException("Reference was not added before");
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This version is not compatible with circular references, but at the same it works like a protection

}

Count--;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, what if you are removing a compound type? for instance, you have an array with 2047 sub items, then you remove the array, the rc will only remove one reference, and the vm will still be full.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

        [TestMethod]
        public void TestNewArrayThenDrop()
        {
            using ScriptBuilder sb = new();
            sb.EmitPush(2040);
            sb.Emit(OpCode.NEWARRAY);
            sb.Emit(OpCode.DROP);
            using ExecutionEngine engine = new();
            engine.LoadScript(sb.ToArray());
            Assert.AreEqual(0, engine.ReferenceCounter.Count);
            Assert.AreEqual(VMState.HALT, engine.Execute());
            Assert.AreEqual(0, engine.ReferenceCounter.Count);
        }

Assert.AreEqual failed. Expected:<0>. Actual:<2040>.
at Neo.Test.UT_ReferenceCounter.TestNewArrayThenDrop() in /Users/jinghuiliao/git/neo/tests/Neo.VM.Tests/UT_ReferenceCounter.cs:line 257
at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor)
at System.Reflection.MethodBaseInvoker.InvokeWithNoArgs(Object obj, BindingFlags invokeAttr)

op:[0000]PUSHINT16 []
op:[0003]NEWARRAY [Integer(2040)]
op:[0004]DROP [Array(2040)]
op:[0005] []

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

        [TestMethod]
        public void TestNewArrayThenDrop()
        {
            using ScriptBuilder sb = new();
            sb.EmitPush(2040);
            sb.Emit(OpCode.NEWARRAY);
            sb.Emit(OpCode.DROP);
            sb.EmitPush(10);
            sb.Emit(OpCode.NEWARRAY);
            using ExecutionEngine engine = new();
            engine.LoadScript(sb.ToArray());
            Assert.AreEqual(0, engine.ReferenceCounter.Count);
            var res = engine.Execute();
            Assert.AreEqual(0, engine.ReferenceCounter.Count);
            Assert.AreEqual(VMState.HALT, res);
        }

Assert.AreEqual failed. Expected:<0>. Actual:<2051>.
at Neo.Test.UT_ReferenceCounter.TestNewArrayThenDrop() in /Users/jinghuiliao/git/neo/tests/Neo.VM.Tests/UT_ReferenceCounter.cs:line 259
at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor)
at System.Reflection.MethodBaseInvoker.InvokeWithNoArgs(Object obj, BindingFlags invokeAttr)

op:[0000]PUSHINT16 []
op:[0003]NEWARRAY [Integer(2040)]
op:[0004]DROP [Array(2040)]
op:[0005]PUSH10 []
op:[0006]NEWARRAY [Integer(10)]

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, what if you are removing a compound type? for instance, you have an array with 2047 sub items, then you remove the array, the rc will only remove one reference, and the vm will still be full.

yes, we need to propagate this change

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add these UT?

Copy link
Member Author

@shargon shargon Nov 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assert.AreEqual(0, engine.ReferenceCounter.Count);
Assert.AreEqual(VMState.HALT, res);

it's 11 in the previous version

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

        [TestMethod]
        public void TestNewArrayThenDrop()
        {
            using ScriptBuilder sb = new();
            sb.EmitPush(2040);
            sb.Emit(OpCode.NEWARRAY);
            sb.Emit(OpCode.DROP);
            using ExecutionEngine engine = new();
            engine.LoadScript(sb.ToArray());
            Assert.AreEqual(0, engine.ReferenceCounter.Count);
            Assert.AreEqual(VMState.HALT, engine.Execute());
            Assert.AreEqual(0, engine.ReferenceCounter.Count);
        }

Assert.AreEqual failed. Expected:<0>. Actual:<2040>. at Neo.Test.UT_ReferenceCounter.TestNewArrayThenDrop() in /Users/jinghuiliao/git/neo/tests/Neo.VM.Tests/UT_ReferenceCounter.cs:line 257 at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor) at System.Reflection.MethodBaseInvoker.InvokeWithNoArgs(Object obj, BindingFlags invokeAttr)

op:[0000]PUSHINT16 [] op:[0003]NEWARRAY [Integer(2040)] op:[0004]DROP [Array(2040)] op:[0005] []

this use the previous version also

}

public void AddReference(StackItem item, CompoundType parent) => AddStackReference(item);
public void RemoveReference(StackItem item, CompoundType parent) => RemoveStackReference(item);
public void AddZeroReferred(StackItem item)
{
// This version don't use this method
}

/// <summary>
/// Checks and processes items that have zero references.
/// </summary>
/// <returns>The current reference count.</returns>
public int CheckZeroReferred()
{
return Count;
}
}
}
20 changes: 20 additions & 0 deletions tests/Neo.VM.Tests/UT_ReferenceCounter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ public void TestArrayNoPush()
using ExecutionEngine engine = new();
engine.LoadScript(sb.ToArray());
Assert.AreEqual(0, engine.ReferenceCounter.Count);

shargon marked this conversation as resolved.
Show resolved Hide resolved
Array array = new(engine.ReferenceCounter, new StackItem[] { 1, 2, 3, 4 });
Assert.AreEqual(array.Count, engine.ReferenceCounter.Count);
Assert.AreEqual(VMState.HALT, engine.Execute());
Expand All @@ -259,5 +260,24 @@ public void TestInvalidReferenceStackItem()
arr.Add(arr2);
Assert.AreEqual(11, reference.Count);
}

[TestMethod]
public void TestDup()
{
using ScriptBuilder sb = new();
sb.Emit(OpCode.PUSH1);
sb.Emit(OpCode.DUP);

using ExecutionEngine engine = new();
engine.LoadScript(sb.ToArray());
Assert.AreEqual(0, engine.ReferenceCounter.Count);
engine.ExecuteNext();
Assert.AreEqual(1, engine.ReferenceCounter.Count);
engine.ExecuteNext();
Assert.AreEqual(2, engine.ReferenceCounter.Count);
engine.ExecuteNext();
Assert.AreEqual(2, engine.ReferenceCounter.Count);
Assert.AreEqual(VMState.HALT, engine.Execute());
}
}
}
284 changes: 284 additions & 0 deletions tests/Neo.VM.Tests/UT_ReferenceCounterV2.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,284 @@
// Copyright (C) 2015-2024 The Neo Project.
//
// UT_ReferenceCounter.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 Microsoft.VisualStudio.TestTools.UnitTesting;
using Neo.VM;
using Neo.VM.Types;
using System;
using Array = Neo.VM.Types.Array;

namespace Neo.Test
{
[TestClass]
public class UT_ReferenceCounterV2
{
[TestMethod]
public void TestCircularReferences()
{
using ScriptBuilder sb = new();
sb.Emit(OpCode.INITSSLOT, new byte[] { 1 }); //{}|{null}:1
sb.EmitPush(0); //{0}|{null}:2
sb.Emit(OpCode.NEWARRAY); //{A[]}|{null}:2
sb.Emit(OpCode.DUP); //{A[],A[]}|{null}:3
sb.Emit(OpCode.DUP); //{A[],A[],A[]}|{null}:4
sb.Emit(OpCode.APPEND); //{A[A]}|{null}:3
sb.Emit(OpCode.DUP); //{A[A],A[A]}|{null}:4
sb.EmitPush(0); //{A[A],A[A],0}|{null}:5
sb.Emit(OpCode.NEWARRAY); //{A[A],A[A],B[]}|{null}:5
sb.Emit(OpCode.STSFLD0); //{A[A],A[A]}|{B[]}:4
sb.Emit(OpCode.LDSFLD0); //{A[A],A[A],B[]}|{B[]}:5
sb.Emit(OpCode.APPEND); //{A[A,B]}|{B[]}:4
sb.Emit(OpCode.LDSFLD0); //{A[A,B],B[]}|{B[]}:5
sb.EmitPush(0); //{A[A,B],B[],0}|{B[]}:6
sb.Emit(OpCode.NEWARRAY); //{A[A,B],B[],C[]}|{B[]}:6
sb.Emit(OpCode.TUCK); //{A[A,B],C[],B[],C[]}|{B[]}:7
sb.Emit(OpCode.APPEND); //{A[A,B],C[]}|{B[C]}:6
sb.EmitPush(0); //{A[A,B],C[],0}|{B[C]}:7
sb.Emit(OpCode.NEWARRAY); //{A[A,B],C[],D[]}|{B[C]}:7
sb.Emit(OpCode.TUCK); //{A[A,B],D[],C[],D[]}|{B[C]}:8
sb.Emit(OpCode.APPEND); //{A[A,B],D[]}|{B[C[D]]}:7
sb.Emit(OpCode.LDSFLD0); //{A[A,B],D[],B[C]}|{B[C[D]]}:8
sb.Emit(OpCode.APPEND); //{A[A,B]}|{B[C[D[B]]]}:7
sb.Emit(OpCode.PUSHNULL); //{A[A,B],null}|{B[C[D[B]]]}:8
sb.Emit(OpCode.STSFLD0); //{A[A,B[C[D[B]]]]}|{null}:7
sb.Emit(OpCode.DUP); //{A[A,B[C[D[B]]]],A[A,B]}|{null}:8
sb.EmitPush(1); //{A[A,B[C[D[B]]]],A[A,B],1}|{null}:9
sb.Emit(OpCode.REMOVE); //{A[A]}|{null}:3
sb.Emit(OpCode.STSFLD0); //{}|{A[A]}:2
sb.Emit(OpCode.RET); //{}:0

using ExecutionEngine engine = new(null, new ReferenceCounterV2(), ExecutionEngineLimits.Default);
Debugger debugger = new(engine);
engine.LoadScript(sb.ToArray());
Assert.AreEqual(VMState.BREAK, debugger.StepInto());
Assert.AreEqual(1, engine.ReferenceCounter.Count);
Assert.AreEqual(VMState.BREAK, debugger.StepInto());
Assert.AreEqual(2, engine.ReferenceCounter.Count);
Assert.AreEqual(VMState.BREAK, debugger.StepInto());
Assert.AreEqual(2, engine.ReferenceCounter.Count);
Assert.AreEqual(VMState.BREAK, debugger.StepInto());
Assert.AreEqual(3, engine.ReferenceCounter.Count);
Assert.AreEqual(VMState.BREAK, debugger.StepInto());
Assert.AreEqual(4, engine.ReferenceCounter.Count);
Assert.AreEqual(VMState.BREAK, debugger.StepInto());
Assert.AreEqual(3, engine.ReferenceCounter.Count);
Assert.AreEqual(VMState.BREAK, debugger.StepInto());
Assert.AreEqual(4, engine.ReferenceCounter.Count);
Assert.AreEqual(VMState.BREAK, debugger.StepInto());
Assert.AreEqual(5, engine.ReferenceCounter.Count);
Assert.AreEqual(VMState.BREAK, debugger.StepInto());
Assert.AreEqual(5, engine.ReferenceCounter.Count);
Assert.AreEqual(VMState.BREAK, debugger.StepInto());
Assert.AreEqual(4, engine.ReferenceCounter.Count);
Assert.AreEqual(VMState.BREAK, debugger.StepInto());
Assert.AreEqual(5, engine.ReferenceCounter.Count);
Assert.AreEqual(VMState.BREAK, debugger.StepInto());
Assert.AreEqual(4, engine.ReferenceCounter.Count);
Assert.AreEqual(VMState.BREAK, debugger.StepInto());
Assert.AreEqual(5, engine.ReferenceCounter.Count);
Assert.AreEqual(VMState.BREAK, debugger.StepInto());
Assert.AreEqual(6, engine.ReferenceCounter.Count);
Assert.AreEqual(VMState.BREAK, debugger.StepInto());
Assert.AreEqual(6, engine.ReferenceCounter.Count);
Assert.AreEqual(VMState.BREAK, debugger.StepInto());
Assert.AreEqual(7, engine.ReferenceCounter.Count);
Assert.AreEqual(VMState.BREAK, debugger.StepInto());
Assert.AreEqual(6, engine.ReferenceCounter.Count);
Assert.AreEqual(VMState.BREAK, debugger.StepInto());
Assert.AreEqual(7, engine.ReferenceCounter.Count);
Assert.AreEqual(VMState.BREAK, debugger.StepInto());
Assert.AreEqual(7, engine.ReferenceCounter.Count);
Assert.AreEqual(VMState.BREAK, debugger.StepInto());
Assert.AreEqual(8, engine.ReferenceCounter.Count);
Assert.AreEqual(VMState.BREAK, debugger.StepInto());
Assert.AreEqual(7, engine.ReferenceCounter.Count);
Assert.AreEqual(VMState.BREAK, debugger.StepInto());
Assert.AreEqual(8, engine.ReferenceCounter.Count);
Assert.AreEqual(VMState.BREAK, debugger.StepInto());
Assert.AreEqual(7, engine.ReferenceCounter.Count);
Assert.AreEqual(VMState.BREAK, debugger.StepInto());
Assert.AreEqual(8, engine.ReferenceCounter.Count);
Assert.AreEqual(VMState.BREAK, debugger.StepInto());
Assert.AreEqual(7, engine.ReferenceCounter.Count);
Assert.AreEqual(VMState.BREAK, debugger.StepInto());
Assert.AreEqual(8, engine.ReferenceCounter.Count);
Assert.AreEqual(VMState.BREAK, debugger.StepInto());
Assert.AreEqual(9, engine.ReferenceCounter.Count);
Assert.AreEqual(VMState.BREAK, debugger.StepInto());
Assert.AreEqual(6, engine.ReferenceCounter.Count);
Assert.AreEqual(VMState.BREAK, debugger.StepInto());
Assert.AreEqual(5, engine.ReferenceCounter.Count);
Assert.AreEqual(VMState.HALT, debugger.Execute());
Assert.AreEqual(4, engine.ReferenceCounter.Count);
}

[TestMethod]
public void TestRemoveReferrer()
{
using ScriptBuilder sb = new();
sb.Emit(OpCode.INITSSLOT, new byte[] { 1 }); //{}|{null}:1
sb.EmitPush(0); //{0}|{null}:2
sb.Emit(OpCode.NEWARRAY); //{A[]}|{null}:2
sb.Emit(OpCode.DUP); //{A[],A[]}|{null}:3
sb.EmitPush(0); //{A[],A[],0}|{null}:4
sb.Emit(OpCode.NEWARRAY); //{A[],A[],B[]}|{null}:4
sb.Emit(OpCode.STSFLD0); //{A[],A[]}|{B[]}:3
sb.Emit(OpCode.LDSFLD0); //{A[],A[],B[]}|{B[]}:4
sb.Emit(OpCode.APPEND); //{A[B]}|{B[]}:3
sb.Emit(OpCode.DROP); //{}|{B[]}:1
sb.Emit(OpCode.RET); //{}:0

using ExecutionEngine engine = new(null, new ReferenceCounterV2(), ExecutionEngineLimits.Default);
Debugger debugger = new(engine);
engine.LoadScript(sb.ToArray());
Assert.AreEqual(VMState.BREAK, debugger.StepInto());
Assert.AreEqual(1, engine.ReferenceCounter.Count);
Assert.AreEqual(VMState.BREAK, debugger.StepInto());
Assert.AreEqual(2, engine.ReferenceCounter.Count);
Assert.AreEqual(VMState.BREAK, debugger.StepInto());
Assert.AreEqual(2, engine.ReferenceCounter.Count);
Assert.AreEqual(VMState.BREAK, debugger.StepInto());
Assert.AreEqual(3, engine.ReferenceCounter.Count);
Assert.AreEqual(VMState.BREAK, debugger.StepInto());
Assert.AreEqual(4, engine.ReferenceCounter.Count);
Assert.AreEqual(VMState.BREAK, debugger.StepInto());
Assert.AreEqual(4, engine.ReferenceCounter.Count);
Assert.AreEqual(VMState.BREAK, debugger.StepInto());
Assert.AreEqual(3, engine.ReferenceCounter.Count);
Assert.AreEqual(VMState.BREAK, debugger.StepInto());
Assert.AreEqual(4, engine.ReferenceCounter.Count);
Assert.AreEqual(VMState.BREAK, debugger.StepInto());
Assert.AreEqual(3, engine.ReferenceCounter.Count);
Assert.AreEqual(VMState.BREAK, debugger.StepInto());
Assert.AreEqual(2, engine.ReferenceCounter.Count);
Assert.AreEqual(VMState.HALT, debugger.Execute());
Assert.AreEqual(1, engine.ReferenceCounter.Count);
}

[TestMethod]
public void TestCheckZeroReferredWithArray()
{
using ScriptBuilder sb = new();

sb.EmitPush(ExecutionEngineLimits.Default.MaxStackSize - 1);
sb.Emit(OpCode.NEWARRAY);

// Good with MaxStackSize

using (ExecutionEngine engine = new(null, new ReferenceCounterV2(), ExecutionEngineLimits.Default))
{
engine.LoadScript(sb.ToArray());
Assert.AreEqual(0, engine.ReferenceCounter.Count);

Assert.AreEqual(VMState.HALT, engine.Execute());
Assert.AreEqual((int)ExecutionEngineLimits.Default.MaxStackSize, engine.ReferenceCounter.Count);
}

// Fault with MaxStackSize+1

sb.Emit(OpCode.PUSH1);

using (ExecutionEngine engine = new(null, new ReferenceCounterV2(), ExecutionEngineLimits.Default))
{
engine.LoadScript(sb.ToArray());
Assert.AreEqual(0, engine.ReferenceCounter.Count);

Assert.AreEqual(VMState.FAULT, engine.Execute());
Assert.AreEqual((int)ExecutionEngineLimits.Default.MaxStackSize + 1, engine.ReferenceCounter.Count);
}
}

[TestMethod]
public void TestCheckZeroReferred()
{
using ScriptBuilder sb = new();

for (int x = 0; x < ExecutionEngineLimits.Default.MaxStackSize; x++)
sb.Emit(OpCode.PUSH1);

// Good with MaxStackSize

using (ExecutionEngine engine = new(null, new ReferenceCounterV2(), ExecutionEngineLimits.Default))
{
engine.LoadScript(sb.ToArray());
Assert.AreEqual(0, engine.ReferenceCounter.Count);

Assert.AreEqual(VMState.HALT, engine.Execute());
Assert.AreEqual((int)ExecutionEngineLimits.Default.MaxStackSize, engine.ReferenceCounter.Count);
}

// Fault with MaxStackSize+1

sb.Emit(OpCode.PUSH1);

using (ExecutionEngine engine = new(null, new ReferenceCounterV2(), ExecutionEngineLimits.Default))
{
engine.LoadScript(sb.ToArray());
Assert.AreEqual(0, engine.ReferenceCounter.Count);

Assert.AreEqual(VMState.FAULT, engine.Execute());
Assert.AreEqual((int)ExecutionEngineLimits.Default.MaxStackSize + 1, engine.ReferenceCounter.Count);
}
}

[TestMethod]
public void TestArrayNoPush()
{
using ScriptBuilder sb = new();
sb.Emit(OpCode.RET);

using ExecutionEngine engine = new(null, new ReferenceCounterV2(), ExecutionEngineLimits.Default);
engine.LoadScript(sb.ToArray());
Assert.AreEqual(0, engine.ReferenceCounter.Count);

Array array = new(engine.ReferenceCounter, new StackItem[] { 1, 2, 3, 4 });
Assert.AreEqual(array.Count, engine.ReferenceCounter.Count);
Assert.AreEqual(VMState.HALT, engine.Execute());
Assert.AreEqual(array.Count, engine.ReferenceCounter.Count);
}

[TestMethod]
[ExpectedException(typeof(InvalidOperationException))]
public void TestInvalidReferenceStackItem()
{
var reference = new ReferenceCounterV2();
var arr = new Array(reference);
var arr2 = new Array();

for (var i = 0; i < 10; i++)
{
arr2.Add(i);
}

arr.Add(arr2);
Assert.AreEqual(11, reference.Count);
}

[TestMethod]
public void TestDup()
{
using ScriptBuilder sb = new();
sb.Emit(OpCode.PUSH1);
sb.Emit(OpCode.DUP);

using ExecutionEngine engine = new(null, new ReferenceCounterV2(), ExecutionEngineLimits.Default);
engine.LoadScript(sb.ToArray());
Assert.AreEqual(0, engine.ReferenceCounter.Count);
engine.ExecuteNext();
Assert.AreEqual(1, engine.ReferenceCounter.Count);
engine.ExecuteNext();
Assert.AreEqual(2, engine.ReferenceCounter.Count);
engine.ExecuteNext();
Assert.AreEqual(2, engine.ReferenceCounter.Count);
Assert.AreEqual(VMState.HALT, engine.Execute());
}
}
}
Loading