@@ -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