Add support for throw in catch block and inner try with same offset as outer (#8207)

This commit is contained in:
yowl 2020-07-12 15:23:02 -05:00 коммит произвёл GitHub
Родитель 8456a8697e
Коммит 3a61e44371
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
3 изменённых файлов: 65 добавлений и 8 удалений

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

@ -152,7 +152,9 @@ namespace Internal.IL
_ehInfoNode = new EHInfoNode(_mangledName);
}
int curRegion = 0;
foreach (ILExceptionRegion region in ilExceptionRegions.OrderBy(region => region.TryOffset))
foreach (ILExceptionRegion region in ilExceptionRegions.OrderBy(region => region.TryOffset)
.ThenByDescending(region => region.TryLength) // outer regions with the same try offset as inner region first - they will have longer lengths, // WASMTODO, except maybe an inner of try {} catch {} which could still be a problem
.ThenBy(region => region.HandlerOffset))
{
_exceptionRegions[curRegion++] = new ExceptionRegion
{
@ -596,12 +598,17 @@ namespace Internal.IL
/// Returns the most nested try region the current offset is in
/// </summary>
private ExceptionRegion GetCurrentTryRegion()
{
return GetTryRegion(_currentOffset);
}
private ExceptionRegion GetTryRegion(int offset)
{
// Iterate backwards to find the most nested region
for (int i = _exceptionRegions.Length - 1; i >= 0; i--)
{
ILExceptionRegion region = _exceptionRegions[i].ILRegion;
if (IsOffsetContained(_currentOffset - 1, region.TryOffset, region.TryLength))
if (IsOffsetContained(offset - 1, region.TryOffset, region.TryLength))
{
return _exceptionRegions[i];
}
@ -2864,7 +2871,7 @@ namespace Internal.IL
var arguments = new StackEntry[] { new ExpressionEntry(StackValueKind.ObjRef, "managedPtr", managedPtr),
new ExpressionEntry(StackValueKind.Int32, "idxStart", LLVMValueRef.CreateConstInt(LLVMTypeRef.Int32, 0xFFFFFFFFu, false)),
new ExpressionEntry(StackValueKind.Int32, "idxTryLandingStart", LLVMValueRef.CreateConstInt(LLVMTypeRef.Int32, (ulong)tryRegion.ILRegion.TryOffset, false)),
new ExpressionEntry(StackValueKind.Int32, "idxCurrentBlockStart", LLVMValueRef.CreateConstInt(LLVMTypeRef.Int32, (ulong)_currentBasicBlock.StartOffset, false)),
new ExpressionEntry(StackValueKind.NativeInt, "shadowStack", _currentFunclet.GetParam(0)),
new ExpressionEntry(StackValueKind.ByRef, "refFrameIter", ehInfoIterator),
new ExpressionEntry(StackValueKind.ByRef, "tryRegionIdx", tryRegionIdx),
@ -2918,6 +2925,7 @@ namespace Internal.IL
if (_leaveTargets != null)
{
LLVMBasicBlockRef switchReturnBlock = default;
foreach (var leaveTarget in _leaveTargets)
{
var targetBlock = _basicBlocks[leaveTarget];
@ -2926,6 +2934,27 @@ namespace Internal.IL
{
@switch.AddCase(BuildConstInt32(targetBlock.StartOffset), GetLLVMBasicBlockForBlock(targetBlock));
}
else
{
// leave destination is in a different funclet, this happens when an exception is thrown/rethrown from inside a catch handler and the throw is not directly in a try handler
// In this case we need to return out of this funclet to get back to the containing funclet. Logic checks we are actually in a catch funclet as opposed to a finally or the main function funclet
ExceptionRegion currentRegion = GetTryRegion(_currentBasicBlock.StartOffset);
if (currentRegion != null && _currentBasicBlock.StartOffset >= currentRegion.ILRegion.HandlerOffset && _currentBasicBlock.StartOffset < currentRegion.ILRegion.HandlerOffset + currentRegion.ILRegion.HandlerLength
&& currentRegion.ILRegion.Kind == ILExceptionRegionKind.Catch)
{
if (switchReturnBlock == default)
{
switchReturnBlock = _currentFunclet.AppendBasicBlock("SwitchReturn");
}
@switch.AddCase(BuildConstInt32(targetBlock.StartOffset), switchReturnBlock);
}
}
}
if (switchReturnBlock != default)
{
landingPadBuilder.PositionAtEnd(switchReturnBlock);
landingPadBuilder.BuildRet(landingPadBuilder.BuildLoad(leaveDestination, "loadLeaveDest"));
}
}
@ -5221,10 +5250,11 @@ namespace Internal.IL
while (i >= 0)
{
int tryStart = _exceptionRegions[i].ILRegion.TryOffset;
int tryLength = _exceptionRegions[i].ILRegion.TryLength;
for (var j = 0; j < _exceptionRegions.Length; j++)
{
ExceptionRegion exceptionRegion = _exceptionRegions[j];
if (exceptionRegion.ILRegion.TryOffset != tryStart) continue;
if (exceptionRegion.ILRegion.TryOffset != tryStart || exceptionRegion.ILRegion.TryLength != tryLength) continue;
// if (i > 0)
// {
// ExceptionRegion previousClause = _exceptionRegions[i - 1];
@ -5261,8 +5291,7 @@ namespace Internal.IL
builder.EmitCompressedUInt((uint)exceptionRegion.ILRegion.TryOffset);
uint tryLength = (uint)exceptionRegion.ILRegion.TryLength;
builder.EmitCompressedUInt((tryLength << 2) | (uint)clauseKind);
builder.EmitCompressedUInt(((uint)tryLength << 2) | (uint)clauseKind);
RelocType rel = (_compilation.NodeFactory.Target.IsWindows)
? RelocType.IMAGE_REL_BASED_ABSOLUTE

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

@ -37,7 +37,7 @@ namespace System.Runtime
}
// TODO: temporary to try things out, when working look to see how to refactor with FindFirstPassHandler
private static bool FindFirstPassHandlerWasm(object exception, uint idxStart, uint idxTryLandingStart /* the start IL idx of the try region for the landing pad, will use in place of PC */,
private static bool FindFirstPassHandlerWasm(object exception, uint idxStart, uint idxCurrentBlockStart /* the start IL idx of the current block for the landing pad, will use in place of PC */,
void* shadowStack, ref EHClauseIterator clauseIter, out uint tryRegionIdx, out byte* pHandler)
{
pHandler = (byte*)0;
@ -74,7 +74,7 @@ namespace System.Runtime
EHClauseIterator.RhEHClauseKindWasm clauseKind = ehClause._clauseKind;
if (((clauseKind != EHClauseIterator.RhEHClauseKindWasm.RH_EH_CLAUSE_TYPED) &&
(clauseKind != EHClauseIterator.RhEHClauseKindWasm.RH_EH_CLAUSE_FILTER))
|| !ehClause.ContainsCodeOffset(idxTryLandingStart))
|| !ehClause.ContainsCodeOffset(idxCurrentBlockStart))
{
continue;
}

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

@ -1412,6 +1412,8 @@ internal static class Program
TestFilter();
TestFilterNested();
TestCatchAndThrow();
}
private static void TestTryCatchNoException()
@ -1624,6 +1626,32 @@ internal static class Program
EndTest(exceptionFlowSequence == @"In middle catchRunning outer filterIn outer catchRunning inner filterIn inner catch");
}
private static void TestCatchAndThrow()
{
StartTest("Test catch and throw different exception");
int caught = 0;
try
{
try
{
throw new Exception("first");
}
catch
{
caught += 1;
throw new Exception("second");
}
}
catch(Exception e)
{
if(e.Message == "second")
{
caught += 10;
}
}
EndTest(caught == 11);
}
static bool Print(string s)
{
exceptionFlowSequence += $"Running {s} filter";