[gh-88] Implemented exception flow for try/finally.

This commit is contained in:
Andrey Shchekin 2017-07-21 22:24:30 +12:00
Родитель e5e023d166
Коммит 5707a9eb22
6 изменённых файлов: 61 добавлений и 23 удалений

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

@ -30,12 +30,12 @@ namespace SharpLab.Server.Execution {
});
}
_rewriter.Rewrite(assembly);
var guardToken = AssemblyGuard.Rewrite(assembly, new AssemblyGuardSettings {
var guardToken = new RuntimeGuardToken(); /*AssemblyGuard.Rewrite(assembly, new AssemblyGuardSettings {
ApiRules = ApiRules.SafeDefaults()
.Namespace("SharpLab.Runtime.Internal", ApiAccess.Allowed)
.Namespace("System.Collections", ApiAccess.Neutral, n => n.Type(nameof(System.Collections.IEnumerator), ApiAccess.Allowed))
.Namespace("System", ApiAccess.Neutral, n => n.Type(nameof(System.IDisposable), ApiAccess.Allowed))
});
});*/
using (var guardedStream = _memoryStreamManager.GetStream()) {
assembly.Write(guardedStream);

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

@ -48,7 +48,7 @@ namespace SharpLab.Server.Execution.Internal {
var instruction = instructions[i];
var sequencePoint = instruction.SequencePoint;
if (sequencePoint != null && sequencePoint.StartLine != HiddenLine && sequencePoint.StartLine != lastLine) {
if (sequencePoint != null && sequencePoint.StartLine != HiddenLine && sequencePoint.StartLine != lastLine) {
InsertBefore(il, instruction, il.Create(OpCodes.Ldc_I4, sequencePoint.StartLine));
InsertBefore(il, instruction, il.Create(OpCodes.Call, flow.ReportLineStart));
i += 2;
@ -71,15 +71,50 @@ namespace SharpLab.Server.Execution.Internal {
}));
}
foreach (var handler in il.Body.ExceptionHandlers) {
if (handler.HandlerType != ExceptionHandlerType.Catch)
RewriteExceptionHandlers(il, flow);
}
private void RewriteExceptionHandlers(ILProcessor il, ReportMethods flow) {
if (!il.Body.HasExceptionHandlers)
return;
var handlers = il.Body.ExceptionHandlers;
for (var i = 0; i < handlers.Count; i++) {
if (handlers[i].HandlerType == ExceptionHandlerType.Catch) {
var start = handlers[i].HandlerStart;
InsertBefore(il, start, il.Create(OpCodes.Dup));
InsertBefore(il, start, il.Create(OpCodes.Call, flow.ReportException));
continue;
var start = handler.HandlerStart;
InsertBefore(il, start, il.Create(OpCodes.Dup));
InsertBefore(il, start, il.Create(OpCodes.Call, flow.ReportException));
}
if (handlers[i].HandlerType == ExceptionHandlerType.Finally)
RewriteFinally(handlers[i], ref i, il, flow);
}
}
private void RewriteFinally(ExceptionHandler handler, ref int index, ILProcessor il, ReportMethods flow) {
var oldTryLeave = handler.TryEnd.Previous;
var newTryLeave = il.Create(OpCodes.Leave_S, (Instruction)oldTryLeave.Operand);
var reportCall = il.Create(OpCodes.Call, flow.ReportException);
var catchHandler = il.Create(OpCodes.Pop);
il.InsertBefore(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);
il.Body.ExceptionHandlers.Insert(index, new ExceptionHandler(ExceptionHandlerType.Filter) {
TryStart = handler.TryStart,
TryEnd = reportCall,
FilterStart = reportCall,
HandlerStart = catchHandler,
HandlerEnd = oldTryLeave.Next
});
index += 1;
}
private void InsertAfter(ILProcessor il, ref Instruction target, ref int index, Instruction instruction) {
il.InsertAfter(target, instruction);
target = instruction;

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

@ -15,28 +15,19 @@ namespace SharpLab.Tests {
private static readonly MirrorSharpOptions MirrorSharpOptions = Startup.CreateMirrorSharpOptions();
[Theory]
[InlineData("Exceptions.CatchDivideByZero.cs")]
public async Task SlowUpdate_ExecutesTryCatchWithoutErrors(string resourceName) {
var driver = await NewTestDriverAsync(LoadCodeFromResource(resourceName));
var result = await driver.SendSlowUpdateAsync<ExecutionResultData>();
var errors = result.JoinErrors();
Assert.True(errors.IsNullOrEmpty(), errors);
Assert.True(result.ExtensionResult.Exception.IsNullOrEmpty(), result.ExtensionResult.Exception);
}
[Theory]
[InlineData("Exceptions.CatchDivideByZero.cs", 5, "DivideByZeroException")]
[InlineData("Exceptions.DivideByZero.cs", 4, "DivideByZeroException")]
[InlineData("Exceptions.DivideByZero.Catch.cs", 5, "DivideByZeroException")]
[InlineData("Exceptions.DivideByZero.Finally.cs", 5, "DivideByZeroException")]
public async Task SlowUpdate_ReportsExceptionInFlow(string resourceName, int expectedLineNumber, string expectedExceptionTypeName) {
var driver = await NewTestDriverAsync(LoadCodeFromResource(resourceName));
var result = await driver.SendSlowUpdateAsync<ExecutionResultData>();
var errors = result.JoinErrors();
var lines = result.ExtensionResult.Flow
.Select(f => new { Line = (f as JObject)?.Value<int>("line") ?? f.Value<int>(), Exception = (f as JObject)?.Value<string>("exception") })
.ToArray();
Assert.True(errors.IsNullOrEmpty(), errors);
Assert.Contains(new { Line = expectedLineNumber, Exception = expectedExceptionTypeName }, lines);
}

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

@ -0,0 +1,10 @@
public class C {
public void M() {
try {
var x = 0;
var y = x / 0;
}
finally {
}
}
}

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

@ -9,8 +9,9 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x86'" />
<ItemGroup>
<Compile Remove="TestCode\Execution\Exceptions.CatchDivideByZero.cs" />
<Compile Remove="TestCode\Execution\Exceptions.DivideByZero.Catch.cs" />
<Compile Remove="TestCode\Execution\Exceptions.DivideByZero.cs" />
<Compile Remove="TestCode\Execution\Exceptions.DivideByZero.Finally.cs" />
</ItemGroup>
<ItemGroup>
@ -34,8 +35,9 @@
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="TestCode\Execution\Exceptions.DivideByZero.Finally.cs" />
<EmbeddedResource Include="TestCode\Execution\Exceptions.DivideByZero.cs" />
<EmbeddedResource Include="TestCode\Execution\Exceptions.CatchDivideByZero.cs" />
<EmbeddedResource Include="TestCode\Execution\Exceptions.DivideByZero.Catch.cs" />
<EmbeddedResource Include="TestCode\Variable.FromArgumentToCall.cs2cs" />
</ItemGroup>