[gh-216] Fixed edge-case bug in exception rewriter.
This commit is contained in:
Родитель
77bd641b25
Коммит
01077344df
|
@ -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
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче