Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Emit exception blocks for the new ILGenerator #94366

Merged
merged 2 commits into from
Nov 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
buyaa-n marked this conversation as resolved.
Show resolved Hide resolved
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