Skip to content

Commit

Permalink
Emit exception blocks for the new ILGenerator (#94366)
Browse files Browse the repository at this point in the history
* Add Exception blocks support to new ILGenerator

* Remove _currentStackIndex, use _exceptionStack.Count instead
  • Loading branch information
buyaa-n committed Nov 14, 2023
1 parent 735e5ed commit 6ffa548
Show file tree
Hide file tree
Showing 3 changed files with 728 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -198,4 +198,13 @@
<data name="Argument_NotMethodCallOpcode" xml:space="preserve">
<value>The specified opcode cannot be passed to EmitCall.</value>
</data>
<data name="Argument_BadExceptionCodeGen" xml:space="preserve">
<value>Incorrect code generation for exception block.</value>
</data>
<data name="Argument_NotInExceptionBlock" xml:space="preserve">
<value>Not currently in an exception block.</value>
</data>
<data name="Argument_ShouldNotSpecifyExceptionType" xml:space="preserve">
<value>Should not specify exception type for catch clause for filter block.</value>
</data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,22 @@ internal sealed class ILGeneratorImpl : ILGenerator
private readonly MethodBuilder _methodBuilder;
private readonly BlobBuilder _builder;
private readonly InstructionEncoder _il;
private readonly ControlFlowBuilder _cfBuilder;
private bool _hasDynamicStackAllocation;
private int _maxStackSize;
private int _currentStack;
private List<LocalBuilder> _locals = new();
private Dictionary<Label, LabelHandle> _labelTable = new(2);
private List<KeyValuePair<MemberInfo, BlobWriter>> _memberReferences = new();
private List<ExceptionBlock> _exceptionStack = new();

internal ILGeneratorImpl(MethodBuilder methodBuilder, int size)
{
_methodBuilder = methodBuilder;
// For compat, runtime implementation doesn't throw for negative or zero value.
_builder = new BlobBuilder(Math.Max(size, DefaultSize));
_il = new InstructionEncoder(_builder, new ControlFlowBuilder());
_cfBuilder = new ControlFlowBuilder();
_il = new InstructionEncoder(_builder, _cfBuilder);
}

internal int GetMaxStackSize() => _maxStackSize;
Expand All @@ -38,11 +41,144 @@ internal ILGeneratorImpl(MethodBuilder methodBuilder, int size)

public override int ILOffset => _il.Offset;

public override void BeginCatchBlock(Type? exceptionType) => throw new NotImplementedException();
public override void BeginExceptFilterBlock() => throw new NotImplementedException();
public override Label BeginExceptionBlock() => throw new NotImplementedException();
public override void BeginFaultBlock() => throw new NotImplementedException();
public override void BeginFinallyBlock() => throw new NotImplementedException();
public override void BeginCatchBlock(Type? exceptionType)
{
if (_exceptionStack.Count < 1)
{
throw new NotSupportedException(SR.Argument_NotInExceptionBlock);
}

ExceptionBlock currentExBlock = _exceptionStack[_exceptionStack.Count - 1];
if (currentExBlock.State == ExceptionState.Filter)
{
// Filter block should be followed by catch block with null exception type
if (exceptionType != null)
{
throw new ArgumentException(SR.Argument_ShouldNotSpecifyExceptionType);
}

Emit(OpCodes.Endfilter);
MarkLabel(currentExBlock.HandleStart);
}
else
{
ArgumentNullException.ThrowIfNull(exceptionType);

Emit(OpCodes.Leave, currentExBlock.EndLabel);
if (currentExBlock.State == ExceptionState.Try)
{
MarkLabel(currentExBlock.TryEnd);
}
else if (currentExBlock.State == ExceptionState.Catch)
{
MarkLabel(currentExBlock.HandleEnd);
}

currentExBlock.HandleStart = DefineLabel();
currentExBlock.HandleEnd = DefineLabel();
ModuleBuilderImpl module = (ModuleBuilderImpl)_methodBuilder.Module;
_cfBuilder.AddCatchRegion(_labelTable[currentExBlock.TryStart], _labelTable[currentExBlock.TryEnd],
_labelTable[currentExBlock.HandleStart], _labelTable[currentExBlock.HandleEnd], module.GetTypeHandle(exceptionType));
MarkLabel(currentExBlock.HandleStart);
}

currentExBlock.State = ExceptionState.Catch;
}

public override void BeginExceptFilterBlock()
{
if (_exceptionStack.Count < 1)
{
throw new NotSupportedException(SR.Argument_NotInExceptionBlock);
}

ExceptionBlock currentExBlock = _exceptionStack[_exceptionStack.Count - 1];
Emit(OpCodes.Leave, currentExBlock.EndLabel);
if (currentExBlock.State == ExceptionState.Try)
{
MarkLabel(currentExBlock.TryEnd);
}
else if (currentExBlock.State == ExceptionState.Catch)
{
MarkLabel(currentExBlock.HandleEnd);
}

currentExBlock.FilterStart = DefineLabel();
currentExBlock.HandleStart = DefineLabel();
currentExBlock.HandleEnd = DefineLabel();
_cfBuilder.AddFilterRegion(_labelTable[currentExBlock.TryStart], _labelTable[currentExBlock.TryEnd],
_labelTable[currentExBlock.HandleStart], _labelTable[currentExBlock.HandleEnd], _labelTable[currentExBlock.FilterStart]);
currentExBlock.State = ExceptionState.Filter;
MarkLabel(currentExBlock.FilterStart);
}

public override Label BeginExceptionBlock()
{
ExceptionBlock currentExBlock = new ExceptionBlock();
currentExBlock.TryStart = DefineLabel();
currentExBlock.TryEnd = DefineLabel();
currentExBlock.EndLabel = DefineLabel(); // End label of the whole exception block
MarkLabel(currentExBlock.TryStart);
currentExBlock.State = ExceptionState.Try;
_exceptionStack.Add(currentExBlock);
return currentExBlock.EndLabel;
}

public override void BeginFaultBlock()
{
if (_exceptionStack.Count < 1)
{
throw new NotSupportedException(SR.Argument_NotInExceptionBlock);
}

ExceptionBlock currentExBlock = _exceptionStack[_exceptionStack.Count - 1];
Emit(OpCodes.Leave, currentExBlock.EndLabel);
if (currentExBlock.State == ExceptionState.Try)
{
MarkLabel(currentExBlock.TryEnd);
}
else if (currentExBlock.State == ExceptionState.Catch)
{
MarkLabel(currentExBlock.HandleEnd);
}

currentExBlock.HandleStart = DefineLabel();
currentExBlock.HandleEnd = DefineLabel();
_cfBuilder.AddFaultRegion(_labelTable[currentExBlock.TryStart], _labelTable[currentExBlock.TryEnd],
_labelTable[currentExBlock.HandleStart], _labelTable[currentExBlock.HandleEnd]);
currentExBlock.State = ExceptionState.Fault;
MarkLabel(currentExBlock.HandleStart);
}

public override void BeginFinallyBlock()
{
if (_exceptionStack.Count < 1)
{
throw new NotSupportedException(SR.Argument_NotInExceptionBlock);
}

ExceptionBlock currentExBlock = _exceptionStack[_exceptionStack.Count - 1];
Label finallyEndLabel = DefineLabel();
if (currentExBlock.State == ExceptionState.Try)
{
Emit(OpCodes.Leave, finallyEndLabel);
}
else if (currentExBlock.State == ExceptionState.Catch)
{
Emit(OpCodes.Leave, currentExBlock.EndLabel);
MarkLabel(currentExBlock.HandleEnd);
currentExBlock.TryEnd = DefineLabel(); // need to nest the catch block within finally
}

MarkLabel(currentExBlock.TryEnd);
currentExBlock.HandleStart = DefineLabel();
currentExBlock.HandleEnd = finallyEndLabel;
_cfBuilder.AddFinallyRegion(_labelTable[currentExBlock.TryStart], _labelTable[currentExBlock.TryEnd],
_labelTable[currentExBlock.HandleStart], _labelTable[currentExBlock.HandleEnd]);
currentExBlock.State = ExceptionState.Finally;
MarkLabel(currentExBlock.HandleStart);
}

public override void BeginScope() => throw new NotImplementedException();

public override LocalBuilder DeclareLocal(Type localType, bool pinned)
Expand Down Expand Up @@ -398,7 +534,39 @@ private static int GetStackChange(OpCode opcode, MethodInfo methodInfo, Type[]?

public override void EmitCalli(OpCode opcode, CallingConventions callingConvention, Type? returnType, Type[]? parameterTypes, Type[]? optionalParameterTypes) => throw new NotImplementedException();
public override void EmitCalli(OpCode opcode, CallingConvention unmanagedCallConv, Type? returnType, Type[]? parameterTypes) => throw new NotImplementedException();
public override void EndExceptionBlock() => throw new NotImplementedException();

public override void EndExceptionBlock()
{
if (_exceptionStack.Count < 1)
{
throw new NotSupportedException(SR.Argument_NotInExceptionBlock);
}

ExceptionBlock currentExBlock = _exceptionStack[_exceptionStack.Count - 1];
ExceptionState state = currentExBlock.State;
Label endLabel = currentExBlock.EndLabel;

if (state == ExceptionState.Filter || state == ExceptionState.Try)
{
throw new InvalidOperationException(SR.Argument_BadExceptionCodeGen);
}

if (state == ExceptionState.Catch)
{
Emit(OpCodes.Leave, endLabel);
MarkLabel(currentExBlock.HandleEnd);
}
else if (state == ExceptionState.Finally || state == ExceptionState.Fault)
{
Emit(OpCodes.Endfinally);
MarkLabel(currentExBlock.HandleEnd);
}

MarkLabel(endLabel);
currentExBlock.State = ExceptionState.Done;
_exceptionStack.Remove(currentExBlock);
}

public override void EndScope() => throw new NotImplementedException();

public override void MarkLabel(Label loc)
Expand All @@ -415,4 +583,26 @@ public override void MarkLabel(Label loc)

public override void UsingNamespace(string usingNamespace) => throw new NotImplementedException();
}

internal sealed class ExceptionBlock
{
public Label TryStart;
public Label TryEnd;
public Label HandleStart;
public Label HandleEnd;
public Label FilterStart;
public Label EndLabel;
public ExceptionState State;
}

internal enum ExceptionState
{
Undefined,
Try,
Filter,
Catch,
Finally,
Fault,
Done
}
}
Loading

0 comments on commit 6ffa548

Please sign in to comment.