diff --git a/src/Neo.Compiler.CSharp/Optimizer/Analysers/BasicBlock.cs b/src/Neo.Compiler.CSharp/Optimizer/Analysers/BasicBlock.cs index 4b58bc24f..2df3da08c 100644 --- a/src/Neo.Compiler.CSharp/Optimizer/Analysers/BasicBlock.cs +++ b/src/Neo.Compiler.CSharp/Optimizer/Analysers/BasicBlock.cs @@ -14,6 +14,7 @@ using Neo.SmartContract.Manifest; using Neo.VM; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; namespace Neo.Optimizer @@ -24,6 +25,7 @@ namespace Neo.Optimizer /// The end of a basic block can be a jumping instruction, an ENDFINALLY, a RET, etc. /// Instructions in the same basic block can be replaced with more effcient ones. /// + [DebuggerDisplay("BasicBlock addr={startAddr}")] public class BasicBlock { public readonly int startAddr; diff --git a/src/Neo.Compiler.CSharp/Optimizer/Analysers/InstructionCoverage.cs b/src/Neo.Compiler.CSharp/Optimizer/Analysers/InstructionCoverage.cs index d2b8ed7b8..b8cb3007c 100644 --- a/src/Neo.Compiler.CSharp/Optimizer/Analysers/InstructionCoverage.cs +++ b/src/Neo.Compiler.CSharp/Optimizer/Analysers/InstructionCoverage.cs @@ -223,6 +223,10 @@ public BranchType CoverInstruction(int addr, Stack? stack = null, // Here we have the exception not catched if (!coveredMap.TryGetValue(addr, out BranchType value)) throw new BadScriptException($"wrong address {addr}"); + Instruction instruction = script.GetInstruction(addr); + if (jumpTargetToSources.ContainsKey(instruction) && addr != entranceAddr) + // on target of jump, start a new recursion to split basic blocks + return coveredMap[firstNotNopAddr] = CoverInstruction(addr, stack, continueFromBasicBlockEntranceAddr: firstNotNopAddr); if (value != BranchType.UNCOVERED) { if (stackType != TryStackType.FINALLY) @@ -243,10 +247,6 @@ public BranchType CoverInstruction(int addr, Stack? stack = null, // FINALLY is OK, but throwed in previous TRY (without catch) or CATCH return BranchType.THROW; // Do not set coveredMap[entranceAddr] = BranchType.THROW; } - Instruction instruction = script.GetInstruction(addr); - if (jumpTargetToSources.ContainsKey(instruction) && addr != entranceAddr) - // on target of jump, start a new recursion to split basic blocks - return coveredMap[firstNotNopAddr] = CoverInstruction(addr, stack, continueFromBasicBlockEntranceAddr: addr); if (instruction.OpCode != OpCode.NOP) { coveredMap[addr] = BranchType.OK; diff --git a/tests/Neo.Compiler.CSharp.UnitTests/Optimizer/BasicBlockTests.cs b/tests/Neo.Compiler.CSharp.UnitTests/Optimizer/BasicBlockTests.cs new file mode 100644 index 000000000..d3034c359 --- /dev/null +++ b/tests/Neo.Compiler.CSharp.UnitTests/Optimizer/BasicBlockTests.cs @@ -0,0 +1,32 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Optimizer; +using Neo.SmartContract.Testing; +using System.Collections.Generic; +using System.Linq; + +namespace Neo.Compiler.CSharp.UnitTests.Optimizer +{ + [TestClass] + public class BasicBlockTests + { + [TestMethod] + public void Test_BasicBlockIfReturn() + { + //public static BigInteger Factorial(BigInteger a) + //{ + // ExecutionEngine.Assert(a >= 0, "Minus number not supported"); + // if (a >= 2) return a * Factorial(a - 1); + // return 1; + //} + ContractInBasicBlocks contract = new(Contract_Recursion.Nef, Contract_Recursion.Manifest); + List blocks = contract.sortedBasicBlocks; + Assert.AreEqual(blocks[0].nextBlock, blocks[1]); + Assert.AreEqual(blocks[0].jumpTargetBlocks.Count, 1); + Assert.AreEqual(blocks[0].jumpTargetBlocks.First(), blocks[3]); + Assert.AreEqual(blocks[0].instructions.Last().OpCode, VM.OpCode.JMPIFNOT); + Assert.AreEqual(blocks[1].instructions.Last().OpCode, VM.OpCode.CALL); + Assert.AreEqual(blocks[2].nextBlock, null); + Assert.AreEqual(blocks[2].instructions.Last().OpCode, VM.OpCode.RET); + } + } +}