[gh-216] Fixed edge-case bug in exception rewriter.

This commit is contained in:
Andrey Shchekin 2017-12-23 14:17:24 +13:00
Родитель 77bd641b25
Коммит 01077344df
4 изменённых файлов: 38 добавлений и 10 удалений

Просмотреть файл

@ -4,6 +4,10 @@ using Mono.Cecil.Cil;
namespace SharpLab.Server.Execution.Internal {
internal static class CecilExtensions {
public static bool IsLeave(this Code code) {
return code == Code.Leave || code == Code.Leave_S;
}
public static Instruction CreateLdargBest(this ILProcessor il, ParameterReference parameter) {
var index = parameter.Index;
if (il.Body.Method.HasThis)

Просмотреть файл

@ -169,20 +169,29 @@ namespace SharpLab.Server.Execution.Internal {
}
private void RewriteFinally(ExceptionHandler handler, ref int handlerIndex, ILProcessor il, ReportMethods flow) {
var oldTryLeave = handler.TryEnd.Previous;
// for try/finally, the only thing we can do is to
// wrap internals of try into a new try+filter+catch
var outerTryLeave = handler.TryEnd.Previous;
if (!outerTryLeave.OpCode.Code.IsLeave()) {
// in some cases (e.g. exception throw) outer handler does
// not end with `leave` -- but we do need it once we wrap
// that throw
outerTryLeave = il.Create(OpCodes.Leave, handler.TryEnd);
il.InsertBefore(handler.TryEnd, outerTryLeave);
}
var newTryLeave = il.Create(OpCodes.Leave_S, (Instruction)oldTryLeave.Operand);
var innerTryLeave = il.Create(OpCodes.Leave_S, (Instruction)outerTryLeave.Operand);
var reportCall = il.CreateCall(flow.ReportException);
var catchHandler = il.Create(OpCodes.Pop);
InsertBeforeAndRetargetAll(il, oldTryLeave, newTryLeave);
il.InsertBefore(oldTryLeave, reportCall);
il.InsertBefore(oldTryLeave, il.Create(OpCodes.Ldc_I4_0));
il.InsertBefore(oldTryLeave, il.Create(OpCodes.Endfilter));
il.InsertBefore(oldTryLeave, catchHandler);
InsertBeforeAndRetargetAll(il, outerTryLeave, innerTryLeave);
il.InsertBefore(outerTryLeave, reportCall);
il.InsertBefore(outerTryLeave, il.Create(OpCodes.Ldc_I4_0));
il.InsertBefore(outerTryLeave, il.Create(OpCodes.Endfilter));
il.InsertBefore(outerTryLeave, catchHandler);
for (var i = 0; i < handlerIndex; i++) {
il.Body.ExceptionHandlers[i].RetargetAll(oldTryLeave.Next, newTryLeave.Next);
il.Body.ExceptionHandlers[i].RetargetAll(outerTryLeave.Next, innerTryLeave.Next);
}
il.Body.ExceptionHandlers.Insert(handlerIndex, new ExceptionHandler(ExceptionHandlerType.Filter) {
@ -190,7 +199,7 @@ namespace SharpLab.Server.Execution.Internal {
TryEnd = reportCall,
FilterStart = reportCall,
HandlerStart = catchHandler,
HandlerEnd = oldTryLeave.Next
HandlerEnd = outerTryLeave.Next
});
handlerIndex += 1;
}

Просмотреть файл

@ -29,6 +29,7 @@ namespace SharpLab.Tests {
[InlineData("Exception.DivideByZero.Catch.Finally.cs", 5, "DivideByZeroException")]
[InlineData("Exception.DivideByZero.Catch.Finally.WriteLine.cs", 5, "DivideByZeroException", Optimize.Debug)]
[InlineData("Exception.DivideByZero.Catch.Finally.WriteLine.cs", 5, "DivideByZeroException", Optimize.Release)]
[InlineData("Exception.Throw.New.Finally.cs", 8, "Exception", Optimize.Debug)]
public async Task SlowUpdate_ReportsExceptionInFlow(string resourceName, int expectedLineNumber, string expectedExceptionTypeName, string optimize = Optimize.Debug) {
var driver = await NewTestDriverAsync(LoadCodeFromResource(resourceName), optimize: optimize);

Просмотреть файл

@ -0,0 +1,14 @@
using System;
public class C
{
public static void Main()
{
try
{
throw new Exception();
}
finally
{
}
}
}