Skip to content

Commit

Permalink
add trycatch support (#186)
Browse files Browse the repository at this point in the history
* parseTryCatch

* fix try endpos problem

* add parse code for  no catch and no finally

* update trycatch

* add a unittest

* fix compile error on compiler

* fix compiler about trycatch

* fix opcodes

* fix optimizer for try catch

* open optimize for trycatch unittest

* Fix format

* fix UT (#255)

* add try-finally ut (#259)

* add try-finally ut

* format, translate

* fix try-finally and add try-catch

* refactor code

* keep only one try-catch matched

* add commont for exception paramcount.

* Update src/Neo.Compiler.MSIL/MSIL/Converter.cs

* Update src/Neo.Compiler.MSIL/MSIL/Converter.cs

* add try-catch failure ut (#261)

* fix some skip leaves problem.

* Reduce changes

Co-authored-by: Shargon <shargon@gmail.com>
Co-authored-by: ShawnYun <42930111+ShawnYun@users.noreply.github.com>
Co-authored-by: Luchuan <luchuan@neo.org>
  • Loading branch information
4 people committed May 6, 2020
1 parent 5c488d9 commit 9a8ccac
Show file tree
Hide file tree
Showing 8 changed files with 627 additions and 17 deletions.
21 changes: 17 additions & 4 deletions src/Neo.Compiler.MSIL/MSIL/Conv_Multi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1308,21 +1308,34 @@ private int ConvertNewObj(ILMethod from, OpCode src, NeoMethod to)
var _type = (src.tokenUnknown as Mono.Cecil.MethodReference);
if (_type.FullName == "System.Void System.Numerics.BigInteger::.ctor(System.Byte[])")
{
return 0;//donothing;
return 0; // donothing;
}
else if (_type.DeclaringType.FullName.Contains("Exception"))
{
// NeoVM `catch` instruction need one exception parameter
Convert1by1(VM.OpCode.NOP, src, to);

var pcount = _type.Parameters.Count;
for (var i = 0; i < pcount; i++)
//pcount must be 1
//if more then one, drop them.
//if pcount==0,add one.
if (pcount == 0) // If there is no parameter, insert one pararmeter
{
ConvertPushString("usererror", src, to);
}
else if (pcount > 1)
{
Insert1(VM.OpCode.DROP, "", to);
// Keep the first exception parameter
for (var i = 0; i < pcount - 1; i++)
{
Insert1(VM.OpCode.DROP, "", to);
}
}
return 0;
}
var type = _type.Resolve();

//Replace the New Array operation if there is an [OpCode] on the constructor
// Replace the New Array operation if there is an [OpCode] on the constructor
foreach (var m in type.DeclaringType.Methods)
{
if (m.IsConstructor && m.HasCustomAttributes)
Expand Down
104 changes: 95 additions & 9 deletions src/Neo.Compiler.MSIL/MSIL/Converter.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Buffers.Binary;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
Expand Down Expand Up @@ -346,23 +347,83 @@ private void ConvertAddrInMethod(NeoMethod to)
{
if (c.needfix)
{
try
if (c.code == VM.OpCode.TRY_L)
{
var _addr = addrconv[c.srcaddr];
Int32 addroff = (Int32)(_addr - c.addr);
c.bytes = BitConverter.GetBytes(addroff);
c.needfix = false;
var srcCatch = BitConverter.ToInt32(c.bytes, 0);
var srcFinal = BitConverter.ToInt32(c.bytes, 4);
if (srcCatch == -1)
{
var bytesCatch = new byte[] { 0, 0, 0, 0 };
Array.Copy(bytesCatch, 0, c.bytes, 0, 4);
}
else
{
var _addrCatch = addrconv[srcCatch];
Int32 addroffCatch = (Int32)(_addrCatch - c.addr);
var bytesCatch = BitConverter.GetBytes(addroffCatch);
Array.Copy(bytesCatch, 0, c.bytes, 0, 4);
}
if (srcFinal == -1)
{
var bytesFinal = new byte[] { 0, 0, 0, 0 };
Array.Copy(bytesFinal, 0, c.bytes, 4, 4);
}
else
{
var _addrFinal = addrconv[srcFinal];
int addroffFinal = (int)(_addrFinal - c.addr);
var bytesFinal = BitConverter.GetBytes(addroffFinal);
Array.Copy(bytesFinal, 0, c.bytes, 4, 4);
}
}
catch
else
{
throw new Exception("cannot convert addr in: " + to.name + "\r\n");
try
{
var _addr = addrconv[c.srcaddr];
int addroff = (int)(_addr - c.addr);
c.bytes = BitConverter.GetBytes(addroff);
c.needfix = false;
}
catch
{
throw new Exception("cannot convert addr in: " + to.name + "\r\n");
}
}
}
}
}

private int ConvertCode(ILMethod method, OpCode src, NeoMethod to)
{
//add try code
if (method.tryPositions.Contains(src.addr))
{
foreach (var info in method.tryInfos)
{
if (info.addr_Try_Begin == src.addr)
{
if (info.catch_Infos.Count > 1)
throw new Exception("only support one catch for now.");

var buf = new byte[8];
var catchAddr = -1;
if (info.catch_Infos.Count == 1)
{
var first = info.catch_Infos.First().Value;
catchAddr = first.addrBegin;
}
var bytesCatch = BitConverter.GetBytes(catchAddr);
var bytesFinally = BitConverter.GetBytes(info.addr_Finally_Begin);

Array.Copy(bytesCatch, 0, buf, 0, 4);
Array.Copy(bytesFinally, 0, buf, 4, 4);
var trycode = Convert1by1(VM.OpCode.TRY_L, src, to, buf);
trycode.needfix = true;
break;
}
}
}
int skipcount = 0;
switch (src.code)
{
Expand Down Expand Up @@ -479,14 +540,39 @@ private int ConvertCode(ILMethod method, OpCode src, NeoMethod to)
// Address convert required
case CodeEx.Br:
case CodeEx.Br_S:
case CodeEx.Leave:
case CodeEx.Leave_S:
{
var code = Convert1by1(VM.OpCode.JMP_L, src, to, new byte[] { 0, 0, 0, 0 });
code.needfix = true;
code.srcaddr = src.tokenAddr_Index;
}
break;
case CodeEx.Leave:
case CodeEx.Leave_S:
{//will support try catch

var tryinfo = method.GetTryInfo(src.addr, out ILMethod.TryCodeType type);

//leaves in try
//leaves in catch
if (type == ILMethod.TryCodeType.Try || type == ILMethod.TryCodeType.Catch)
{
var code = Convert1by1(VM.OpCode.ENDTRY_L, src, to, new byte[] { 0, 0, 0, 0 });
code.needfix = true;
code.srcaddr = src.tokenAddr_Index;
}
else //or else just jmp
{
var code = Convert1by1(VM.OpCode.JMP_L, src, to, new byte[] { 0, 0, 0, 0 });
code.needfix = true;
code.srcaddr = src.tokenAddr_Index;
}
}
break;
case CodeEx.Endfinally:
{
//need vm add these opcodes
var code = Convert1by1(VM.OpCode.ENDFINALLY, src, to);
}
break;
case CodeEx.Switch:
{
Expand Down
187 changes: 186 additions & 1 deletion src/Neo.Compiler.MSIL/MSIL/ILModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,23 @@ public override string ToString()
}
}

public class ILCatchInfo
{
public int addrBegin;
public int addrEnd;
public string catchType;
}

public class ILTryInfo
{
public int addr_Try_Begin = -1;
public int addr_Try_End = -1;
public int addr_Try_End_F = -1;//IL try catch,try final is 2 different Block,need to process that.
public Dictionary<string, ILCatchInfo> catch_Infos = new Dictionary<string, ILCatchInfo>();
public int addr_Finally_Begin = -1;
public int addr_Finally_End = -1;
}

public class ILMethod
{
public ILType type = null;
Expand All @@ -208,7 +225,8 @@ public class ILMethod
public List<NeoParam> body_Variables = new List<NeoParam>();
public SortedDictionary<int, OpCode> body_Codes = new SortedDictionary<int, OpCode>();
public string fail = null;

public List<ILTryInfo> tryInfos = new List<ILTryInfo>();
public List<int> tryPositions = new List<int>();
public ILMethod(ILType type, Mono.Cecil.MethodDefinition method, ILogger logger = null)
{
this.type = type;
Expand Down Expand Up @@ -269,8 +287,175 @@ public ILMethod(ILType type, Mono.Cecil.MethodDefinition method, ILogger logger
c.InitToken(code.Operand);
this.body_Codes.Add(c.addr, c);
}
if (method.Body.HasExceptionHandlers)
{
var mapTryInfos = new Dictionary<string, ILTryInfo>();
foreach (var e in method.Body.ExceptionHandlers)
{
if (e.HandlerType == Mono.Cecil.Cil.ExceptionHandlerType.Catch)
{
var key = e.TryStart.Offset + "_" + e.TryEnd.Offset;
if (mapTryInfos.ContainsKey(key) == false)
mapTryInfos[key] = new ILTryInfo();

var tryinfo = mapTryInfos[key];
tryinfo.addr_Try_Begin = e.TryStart.Offset;
tryinfo.addr_Try_End = e.TryEnd.Offset;

var catchtypestr = e.CatchType.FullName;

tryinfo.catch_Infos[catchtypestr] = new ILCatchInfo()
{
addrBegin = e.HandlerStart.Offset,
addrEnd = e.HandlerEnd.Offset,
catchType = catchtypestr
};
}
else if (e.HandlerType == Mono.Cecil.Cil.ExceptionHandlerType.Finally)
{
int start = e.TryStart.Offset;
int end = e.TryEnd.Offset;
Mono.Cecil.Cil.ExceptionHandler handler = null;
foreach (var tryinfocatch in method.Body.ExceptionHandlers)
{
if (tryinfocatch.HandlerType == Mono.Cecil.Cil.ExceptionHandlerType.Catch && tryinfocatch.TryStart.Offset == e.TryStart.Offset && tryinfocatch.TryEnd.Offset <= e.TryEnd.Offset)
{//find include catch element.
if (handler == null)
handler = tryinfocatch;
else if (handler.TryEnd.Offset < tryinfocatch.TryEnd.Offset)
handler = tryinfocatch;
}
}
if (handler != null)
{
start = handler.TryStart.Offset;
end = handler.TryEnd.Offset;
}

var key = start + "_" + end;

if (mapTryInfos.ContainsKey(key) == false)
mapTryInfos[key] = new ILTryInfo();

var tryinfo = mapTryInfos[key];
tryinfo.addr_Try_Begin = start;
tryinfo.addr_Try_End = end;
tryinfo.addr_Try_End_F = e.TryEnd.Offset;

tryinfo.addr_Finally_Begin = e.HandlerStart.Offset;
tryinfo.addr_Finally_End = e.HandlerEnd.Offset;
}
else
{
throw new Exception("not support yet." + e.HandlerType);
}
}
this.tryInfos = new List<ILTryInfo>(mapTryInfos.Values);
foreach (var info in this.tryInfos)
{
if (this.tryPositions.Contains(info.addr_Try_Begin) == false)
this.tryPositions.Add(info.addr_Try_Begin);
}
}
}
}
}

public enum TryCodeType
{
None,
Try,
Try_Final,
Catch,
Final,
}
public ILTryInfo GetTryInfo(int addr, out TryCodeType type)
{
type = TryCodeType.None;
ILTryInfo last = null;
int begin = -1;
int end = -1;
foreach (var info in tryInfos)
{
//check try first
if (info.addr_Try_Begin <= addr && addr < info.addr_Try_End)
{
if (last == null)
{
type = TryCodeType.Try;
last = info;
begin = info.addr_Try_Begin;
end = last.addr_Try_End;
}
else if (begin <= info.addr_Try_Begin && last.addr_Try_End < end)
{
type = TryCodeType.Try;
last = info;
begin = info.addr_Try_Begin;
end = last.addr_Try_End;
}
}

//then code in final but not in try
if (info.addr_Try_Begin <= addr && addr < info.addr_Try_End_F)
{
if (last == null)
{
type = TryCodeType.Try_Final;
last = info;
begin = info.addr_Try_Begin;
end = last.addr_Try_End_F;
}
else if (begin <= info.addr_Try_Begin && last.addr_Try_End_F < end)
{
type = TryCodeType.Try_Final;
last = info;
begin = info.addr_Try_Begin;
end = last.addr_Try_End_F;
}
}

//find match finally
if (info.addr_Finally_Begin <= addr && addr < info.addr_Finally_End)
{
if (last == null)
{
type = TryCodeType.Final;
last = info;
begin = info.addr_Finally_Begin;
end = last.addr_Finally_End;
}
else if (begin <= info.addr_Finally_Begin && last.addr_Finally_End < end)
{
type = TryCodeType.Final;
last = info;
begin = info.addr_Finally_Begin;
end = last.addr_Finally_End;
}
}
//find match catch
foreach (var c in info.catch_Infos)
{
if (c.Value.addrBegin <= addr && addr < c.Value.addrEnd)
{
if (last == null)
{
type = TryCodeType.Catch;
last = info;
begin = c.Value.addrBegin;
end = c.Value.addrEnd;
}
else if (begin <= c.Value.addrBegin && c.Value.addrBegin < end)
{
type = TryCodeType.Catch;
last = info;
begin = c.Value.addrBegin;
end = c.Value.addrEnd;
}
}
}
}
return last;
}

public int GetLastCodeAddr(int srcaddr)
Expand Down
Loading

0 comments on commit 9a8ccac

Please sign in to comment.