Skip to content

Commit 6ffa548

Browse files
authored
Emit exception blocks for the new ILGenerator (#94366)
* Add Exception blocks support to new ILGenerator * Remove _currentStackIndex, use _exceptionStack.Count instead
1 parent 735e5ed commit 6ffa548

File tree

3 files changed

+728
-10
lines changed

3 files changed

+728
-10
lines changed

src/libraries/System.Reflection.Emit/src/Resources/Strings.resx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,4 +198,13 @@
198198
<data name="Argument_NotMethodCallOpcode" xml:space="preserve">
199199
<value>The specified opcode cannot be passed to EmitCall.</value>
200200
</data>
201+
<data name="Argument_BadExceptionCodeGen" xml:space="preserve">
202+
<value>Incorrect code generation for exception block.</value>
203+
</data>
204+
<data name="Argument_NotInExceptionBlock" xml:space="preserve">
205+
<value>Not currently in an exception block.</value>
206+
</data>
207+
<data name="Argument_ShouldNotSpecifyExceptionType" xml:space="preserve">
208+
<value>Should not specify exception type for catch clause for filter block.</value>
209+
</data>
201210
</root>

src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ILGeneratorImpl.cs

Lines changed: 197 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,22 @@ internal sealed class ILGeneratorImpl : ILGenerator
1515
private readonly MethodBuilder _methodBuilder;
1616
private readonly BlobBuilder _builder;
1717
private readonly InstructionEncoder _il;
18+
private readonly ControlFlowBuilder _cfBuilder;
1819
private bool _hasDynamicStackAllocation;
1920
private int _maxStackSize;
2021
private int _currentStack;
2122
private List<LocalBuilder> _locals = new();
2223
private Dictionary<Label, LabelHandle> _labelTable = new(2);
2324
private List<KeyValuePair<MemberInfo, BlobWriter>> _memberReferences = new();
25+
private List<ExceptionBlock> _exceptionStack = new();
2426

2527
internal ILGeneratorImpl(MethodBuilder methodBuilder, int size)
2628
{
2729
_methodBuilder = methodBuilder;
2830
// For compat, runtime implementation doesn't throw for negative or zero value.
2931
_builder = new BlobBuilder(Math.Max(size, DefaultSize));
30-
_il = new InstructionEncoder(_builder, new ControlFlowBuilder());
32+
_cfBuilder = new ControlFlowBuilder();
33+
_il = new InstructionEncoder(_builder, _cfBuilder);
3134
}
3235

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

3942
public override int ILOffset => _il.Offset;
4043

41-
public override void BeginCatchBlock(Type? exceptionType) => throw new NotImplementedException();
42-
public override void BeginExceptFilterBlock() => throw new NotImplementedException();
43-
public override Label BeginExceptionBlock() => throw new NotImplementedException();
44-
public override void BeginFaultBlock() => throw new NotImplementedException();
45-
public override void BeginFinallyBlock() => throw new NotImplementedException();
44+
public override void BeginCatchBlock(Type? exceptionType)
45+
{
46+
if (_exceptionStack.Count < 1)
47+
{
48+
throw new NotSupportedException(SR.Argument_NotInExceptionBlock);
49+
}
50+
51+
ExceptionBlock currentExBlock = _exceptionStack[_exceptionStack.Count - 1];
52+
if (currentExBlock.State == ExceptionState.Filter)
53+
{
54+
// Filter block should be followed by catch block with null exception type
55+
if (exceptionType != null)
56+
{
57+
throw new ArgumentException(SR.Argument_ShouldNotSpecifyExceptionType);
58+
}
59+
60+
Emit(OpCodes.Endfilter);
61+
MarkLabel(currentExBlock.HandleStart);
62+
}
63+
else
64+
{
65+
ArgumentNullException.ThrowIfNull(exceptionType);
66+
67+
Emit(OpCodes.Leave, currentExBlock.EndLabel);
68+
if (currentExBlock.State == ExceptionState.Try)
69+
{
70+
MarkLabel(currentExBlock.TryEnd);
71+
}
72+
else if (currentExBlock.State == ExceptionState.Catch)
73+
{
74+
MarkLabel(currentExBlock.HandleEnd);
75+
}
76+
77+
currentExBlock.HandleStart = DefineLabel();
78+
currentExBlock.HandleEnd = DefineLabel();
79+
ModuleBuilderImpl module = (ModuleBuilderImpl)_methodBuilder.Module;
80+
_cfBuilder.AddCatchRegion(_labelTable[currentExBlock.TryStart], _labelTable[currentExBlock.TryEnd],
81+
_labelTable[currentExBlock.HandleStart], _labelTable[currentExBlock.HandleEnd], module.GetTypeHandle(exceptionType));
82+
MarkLabel(currentExBlock.HandleStart);
83+
}
84+
85+
currentExBlock.State = ExceptionState.Catch;
86+
}
87+
88+
public override void BeginExceptFilterBlock()
89+
{
90+
if (_exceptionStack.Count < 1)
91+
{
92+
throw new NotSupportedException(SR.Argument_NotInExceptionBlock);
93+
}
94+
95+
ExceptionBlock currentExBlock = _exceptionStack[_exceptionStack.Count - 1];
96+
Emit(OpCodes.Leave, currentExBlock.EndLabel);
97+
if (currentExBlock.State == ExceptionState.Try)
98+
{
99+
MarkLabel(currentExBlock.TryEnd);
100+
}
101+
else if (currentExBlock.State == ExceptionState.Catch)
102+
{
103+
MarkLabel(currentExBlock.HandleEnd);
104+
}
105+
106+
currentExBlock.FilterStart = DefineLabel();
107+
currentExBlock.HandleStart = DefineLabel();
108+
currentExBlock.HandleEnd = DefineLabel();
109+
_cfBuilder.AddFilterRegion(_labelTable[currentExBlock.TryStart], _labelTable[currentExBlock.TryEnd],
110+
_labelTable[currentExBlock.HandleStart], _labelTable[currentExBlock.HandleEnd], _labelTable[currentExBlock.FilterStart]);
111+
currentExBlock.State = ExceptionState.Filter;
112+
MarkLabel(currentExBlock.FilterStart);
113+
}
114+
115+
public override Label BeginExceptionBlock()
116+
{
117+
ExceptionBlock currentExBlock = new ExceptionBlock();
118+
currentExBlock.TryStart = DefineLabel();
119+
currentExBlock.TryEnd = DefineLabel();
120+
currentExBlock.EndLabel = DefineLabel(); // End label of the whole exception block
121+
MarkLabel(currentExBlock.TryStart);
122+
currentExBlock.State = ExceptionState.Try;
123+
_exceptionStack.Add(currentExBlock);
124+
return currentExBlock.EndLabel;
125+
}
126+
127+
public override void BeginFaultBlock()
128+
{
129+
if (_exceptionStack.Count < 1)
130+
{
131+
throw new NotSupportedException(SR.Argument_NotInExceptionBlock);
132+
}
133+
134+
ExceptionBlock currentExBlock = _exceptionStack[_exceptionStack.Count - 1];
135+
Emit(OpCodes.Leave, currentExBlock.EndLabel);
136+
if (currentExBlock.State == ExceptionState.Try)
137+
{
138+
MarkLabel(currentExBlock.TryEnd);
139+
}
140+
else if (currentExBlock.State == ExceptionState.Catch)
141+
{
142+
MarkLabel(currentExBlock.HandleEnd);
143+
}
144+
145+
currentExBlock.HandleStart = DefineLabel();
146+
currentExBlock.HandleEnd = DefineLabel();
147+
_cfBuilder.AddFaultRegion(_labelTable[currentExBlock.TryStart], _labelTable[currentExBlock.TryEnd],
148+
_labelTable[currentExBlock.HandleStart], _labelTable[currentExBlock.HandleEnd]);
149+
currentExBlock.State = ExceptionState.Fault;
150+
MarkLabel(currentExBlock.HandleStart);
151+
}
152+
153+
public override void BeginFinallyBlock()
154+
{
155+
if (_exceptionStack.Count < 1)
156+
{
157+
throw new NotSupportedException(SR.Argument_NotInExceptionBlock);
158+
}
159+
160+
ExceptionBlock currentExBlock = _exceptionStack[_exceptionStack.Count - 1];
161+
Label finallyEndLabel = DefineLabel();
162+
if (currentExBlock.State == ExceptionState.Try)
163+
{
164+
Emit(OpCodes.Leave, finallyEndLabel);
165+
}
166+
else if (currentExBlock.State == ExceptionState.Catch)
167+
{
168+
Emit(OpCodes.Leave, currentExBlock.EndLabel);
169+
MarkLabel(currentExBlock.HandleEnd);
170+
currentExBlock.TryEnd = DefineLabel(); // need to nest the catch block within finally
171+
}
172+
173+
MarkLabel(currentExBlock.TryEnd);
174+
currentExBlock.HandleStart = DefineLabel();
175+
currentExBlock.HandleEnd = finallyEndLabel;
176+
_cfBuilder.AddFinallyRegion(_labelTable[currentExBlock.TryStart], _labelTable[currentExBlock.TryEnd],
177+
_labelTable[currentExBlock.HandleStart], _labelTable[currentExBlock.HandleEnd]);
178+
currentExBlock.State = ExceptionState.Finally;
179+
MarkLabel(currentExBlock.HandleStart);
180+
}
181+
46182
public override void BeginScope() => throw new NotImplementedException();
47183

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

399535
public override void EmitCalli(OpCode opcode, CallingConventions callingConvention, Type? returnType, Type[]? parameterTypes, Type[]? optionalParameterTypes) => throw new NotImplementedException();
400536
public override void EmitCalli(OpCode opcode, CallingConvention unmanagedCallConv, Type? returnType, Type[]? parameterTypes) => throw new NotImplementedException();
401-
public override void EndExceptionBlock() => throw new NotImplementedException();
537+
538+
public override void EndExceptionBlock()
539+
{
540+
if (_exceptionStack.Count < 1)
541+
{
542+
throw new NotSupportedException(SR.Argument_NotInExceptionBlock);
543+
}
544+
545+
ExceptionBlock currentExBlock = _exceptionStack[_exceptionStack.Count - 1];
546+
ExceptionState state = currentExBlock.State;
547+
Label endLabel = currentExBlock.EndLabel;
548+
549+
if (state == ExceptionState.Filter || state == ExceptionState.Try)
550+
{
551+
throw new InvalidOperationException(SR.Argument_BadExceptionCodeGen);
552+
}
553+
554+
if (state == ExceptionState.Catch)
555+
{
556+
Emit(OpCodes.Leave, endLabel);
557+
MarkLabel(currentExBlock.HandleEnd);
558+
}
559+
else if (state == ExceptionState.Finally || state == ExceptionState.Fault)
560+
{
561+
Emit(OpCodes.Endfinally);
562+
MarkLabel(currentExBlock.HandleEnd);
563+
}
564+
565+
MarkLabel(endLabel);
566+
currentExBlock.State = ExceptionState.Done;
567+
_exceptionStack.Remove(currentExBlock);
568+
}
569+
402570
public override void EndScope() => throw new NotImplementedException();
403571

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

416584
public override void UsingNamespace(string usingNamespace) => throw new NotImplementedException();
417585
}
586+
587+
internal sealed class ExceptionBlock
588+
{
589+
public Label TryStart;
590+
public Label TryEnd;
591+
public Label HandleStart;
592+
public Label HandleEnd;
593+
public Label FilterStart;
594+
public Label EndLabel;
595+
public ExceptionState State;
596+
}
597+
598+
internal enum ExceptionState
599+
{
600+
Undefined,
601+
Try,
602+
Filter,
603+
Catch,
604+
Finally,
605+
Fault,
606+
Done
607+
}
418608
}

0 commit comments

Comments
 (0)