Skip to content

Commit

Permalink
correct branch return type
Browse files Browse the repository at this point in the history
  • Loading branch information
Hecate2 committed Feb 28, 2024
1 parent 74fe2fa commit f436f60
Showing 1 changed file with 47 additions and 21 deletions.
68 changes: 47 additions & 21 deletions src/Neo.Compiler.CSharp/Optimizer/Strategies/Reachability.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,13 @@ public enum BranchType
OK, // One of the branches may return without exception
THROW, // All branches surely has exceptions, but can be catched
ABORT, // All branches abort, and cannot be catched
UNCOVERED,
}

[Strategy(Priority = int.MaxValue)]
public static (NefFile, ContractManifest, JObject) RemoveUncoveredInstructions(NefFile nef, ContractManifest manifest, JObject debugInfo)
{
Dictionary<int, bool> coveredMap = FindCoveredInstructions(nef, manifest, debugInfo);
Dictionary<int, BranchType> coveredMap = FindCoveredInstructions(nef, manifest, debugInfo);
Script oldScript = nef.Script;
List<(int, Instruction)> oldAddressAndInstructionsList = oldScript.EnumerateInstructions().ToList();
Dictionary<int, Instruction> oldAddressToInstruction = new();
Expand All @@ -53,7 +54,7 @@ public static (NefFile, ContractManifest, JObject) RemoveUncoveredInstructions(N
int currentAddress = 0;
foreach ((int a, Instruction i) in oldAddressAndInstructionsList)
{
if (coveredMap[a])
if (coveredMap[a] != BranchType.UNCOVERED)
{
simplifiedInstructionsToAddress.Add(i, currentAddress);
currentAddress += i.Size;
Expand Down Expand Up @@ -160,13 +161,13 @@ public static (NefFile, ContractManifest, JObject) RemoveUncoveredInstructions(N
return (nef, manifest, debugInfo);
}

public static Dictionary<int, bool>
public static Dictionary<int, BranchType>
FindCoveredInstructions(NefFile nef, ContractManifest manifest, JToken debugInfo)
{
Script script = nef.Script;
Dictionary<int, bool> coveredMap = new();
Dictionary<int, BranchType> coveredMap = new();
foreach ((int addr, Instruction _) in script.EnumerateInstructions())
coveredMap.Add(addr, false);
coveredMap.Add(addr, BranchType.UNCOVERED);

Dictionary<int, string> publicMethodStartingAddressToName = new();
foreach (ContractMethodDescriptor method in manifest.Abi.Methods)
Expand Down Expand Up @@ -201,8 +202,9 @@ public static Dictionary<int, bool>
/// <returns>Whether it is possible to return without exception</returns>
/// <exception cref="BadScriptException"></exception>
/// <exception cref="NotImplementedException"></exception>
public static BranchType CoverInstruction(int addr, Script script, Dictionary<int, bool> coveredMap, Stack<((int returnAddr, int finallyAddr), TryStack stackType)>? stack = null, bool throwed = false)
public static BranchType CoverInstruction(int addr, Script script, Dictionary<int, BranchType> coveredMap, Stack<((int returnAddr, int finallyAddr), TryStack stackType)>? stack = null, bool throwed = false)
{
int entranceAddr = addr;
stack ??= new();
if (stack.Count == 0)
stack.Push(((-1, -1), TryStack.ENTRY));
Expand Down Expand Up @@ -244,25 +246,32 @@ public static BranchType CoverInstruction(int addr, Script script, Dictionary<in
HANDLE_NORMAL_CASE:
if (!coveredMap.ContainsKey(addr))
throw new BadScriptException($"wrong address {addr}");
if (coveredMap[addr])
if (coveredMap[addr] != BranchType.UNCOVERED)
// We have visited the code. Skip it.
return BranchType.OK;
return coveredMap[addr];
Instruction instruction = script.GetInstruction(addr);
if (instruction.OpCode != OpCode.NOP)
coveredMap[addr] = true;
coveredMap[addr] = BranchType.OK;

// TODO: ABORTMSG may THROW instead of ABORT. Just throw new NotImplementedException for ABORTMSG?
if (instruction.OpCode == OpCode.ABORT || instruction.OpCode == OpCode.ABORTMSG)
{
// See if we are in a try. There may still be runtime exceptions
((catchAddr, finallyAddr), stackType) = stack.Peek();
if (stackType == TryStack.TRY && catchAddr != -1)
{
// Visit catchAddr because there may still be exceptions at runtime
return CoverInstruction(catchAddr, script, coveredMap, stack: new(stack.Reverse()), throwed: true);
coveredMap[entranceAddr] = CoverInstruction(catchAddr, script, coveredMap, stack: new(stack.Reverse()), throwed: true);
return coveredMap[entranceAddr];
}
if (stackType == TryStack.CATCH && finallyAddr != -1)
{
// Visit finallyAddr because there may still be exceptions at runtime
return CoverInstruction(finallyAddr, script, coveredMap, stack: new(stack.Reverse()), throwed: true);
return BranchType.ABORT;
coveredMap[entranceAddr] = CoverInstruction(finallyAddr, script, coveredMap, stack: new(stack.Reverse()), throwed: true);
return coveredMap[entranceAddr];
}
coveredMap[entranceAddr] = BranchType.ABORT;
return coveredMap[entranceAddr];
}
if (callWithJump.Contains(instruction.OpCode))
{
Expand All @@ -278,12 +287,19 @@ public static BranchType CoverInstruction(int addr, Script script, Dictionary<in
// See if we are in a try. There may still be runtime exceptions
((catchAddr, finallyAddr), stackType) = stack.Peek();
if (stackType == TryStack.TRY && catchAddr != -1)
{
// Visit catchAddr because there may still be exceptions at runtime
return CoverInstruction(catchAddr, script, coveredMap, stack: new(stack.Reverse()), throwed: true);
coveredMap[entranceAddr] = CoverInstruction(catchAddr, script, coveredMap, stack: new(stack.Reverse()), throwed: true);
return coveredMap[entranceAddr];
}
if (stackType == TryStack.CATCH && finallyAddr != -1)
{
// Visit finallyAddr because there may still be exceptions at runtime
return CoverInstruction(finallyAddr, script, coveredMap, stack: new(stack.Reverse()), throwed: true);
return BranchType.ABORT;
coveredMap[entranceAddr] = CoverInstruction(finallyAddr, script, coveredMap, stack: new(stack.Reverse()), throwed: true);
return coveredMap[entranceAddr];
}
coveredMap[entranceAddr] = BranchType.ABORT;
return coveredMap[entranceAddr];
}
if (returnedType == BranchType.THROW)
goto HANDLE_THROW;
Expand All @@ -298,7 +314,8 @@ public static BranchType CoverInstruction(int addr, Script script, Dictionary<in
if (stackType == TryStack.CATCH && finallyAddr != -1)
// Visit finallyAddr because there may still be exceptions at runtime
CoverInstruction(finallyAddr, script, coveredMap, stack: new(stack.Reverse()), throwed: true);
return BranchType.OK;
coveredMap[entranceAddr] = BranchType.OK;
return coveredMap[entranceAddr];
}
if (tryThrowFinally.Contains(instruction.OpCode))
{
Expand Down Expand Up @@ -373,19 +390,27 @@ public static BranchType CoverInstruction(int addr, Script script, Dictionary<in
if (stackType == TryStack.CATCH && finallyAddr != -1)
// Visit finallyAddr because there may still be exceptions at runtime
CoverInstruction(finallyAddr, script, coveredMap, stack: new(stack.Reverse()), throwed: true);
return BranchType.OK;
coveredMap[entranceAddr] = BranchType.OK;
return coveredMap[entranceAddr];
}
if (noJump == BranchType.ABORT && jump == BranchType.ABORT)
{
// See if we are in a try. There may still be runtime exceptions
((catchAddr, finallyAddr), stackType) = stack.Peek();
if (stackType == TryStack.TRY && catchAddr != -1)
{
// Visit catchAddr because there may still be exceptions at runtime
return CoverInstruction(catchAddr, script, coveredMap, stack: new(stack.Reverse()), throwed: true);
coveredMap[entranceAddr] = CoverInstruction(catchAddr, script, coveredMap, stack: new(stack.Reverse()), throwed: true);
return coveredMap[entranceAddr];
}
if (stackType == TryStack.CATCH && finallyAddr != -1)
{
// Visit finallyAddr because there may still be exceptions at runtime
return CoverInstruction(finallyAddr, script, coveredMap, stack: new(stack.Reverse()), throwed: true);
return BranchType.ABORT;
coveredMap[entranceAddr] = CoverInstruction(finallyAddr, script, coveredMap, stack: new(stack.Reverse()), throwed: true);
return coveredMap[entranceAddr];
}
coveredMap[entranceAddr] = BranchType.ABORT;
return coveredMap[entranceAddr];
}
if (noJump == BranchType.THROW || jump == BranchType.THROW) // THROW, ABORT => THROW
goto HANDLE_THROW;
Expand All @@ -394,7 +419,8 @@ public static BranchType CoverInstruction(int addr, Script script, Dictionary<in

addr += instruction.Size;
}
return throwed ? BranchType.THROW : BranchType.OK;
coveredMap[entranceAddr] = throwed ? BranchType.THROW : BranchType.OK;
return coveredMap[entranceAddr];
}
}
}

0 comments on commit f436f60

Please sign in to comment.