+ ILGenerator ig = ec.ig;
+
+ EmitPreTryBody (ec);
+
+ if (resume_points != null) {
+ IntConstant.EmitInt (ig, (int) Iterator.State.Running);
+ ig.Emit (OpCodes.Stloc, ec.CurrentIterator.CurrentPC);
+ }
+
+ ig.BeginExceptionBlock ();
+
+ if (resume_points != null) {
+ ig.MarkLabel (resume_point);
+
+ // For normal control flow, we want to fall-through the Switch
+ // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
+ ig.Emit (OpCodes.Ldloc, ec.CurrentIterator.CurrentPC);
+ IntConstant.EmitInt (ig, first_resume_pc);
+ ig.Emit (OpCodes.Sub);
+
+ Label [] labels = new Label [resume_points.Count];
+ for (int i = 0; i < resume_points.Count; ++i)
+ labels [i] = ((ResumableStatement) resume_points [i]).PrepareForEmit (ec);
+ ig.Emit (OpCodes.Switch, labels);
+ }
+
+ EmitTryBody (ec);
+
+ ig.BeginFinallyBlock ();
+
+ Label start_finally = ec.ig.DefineLabel ();
+ if (resume_points != null) {
+ ig.Emit (OpCodes.Ldloc, ec.CurrentIterator.SkipFinally);
+ ig.Emit (OpCodes.Brfalse_S, start_finally);
+ ig.Emit (OpCodes.Endfinally);
+ }
+
+ ig.MarkLabel (start_finally);
+ EmitFinallyBody (ec);
+
+ ig.EndExceptionBlock ();
+ }
+
+ public void SomeCodeFollows ()
+ {
+ code_follows = true;
+ }
+
+ protected void ResolveReachability (EmitContext ec)
+ {
+ // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
+ // So, ensure there's some IL code after this statement.
+ if (!code_follows && resume_points == null && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
+ ec.NeedReturnLabel ();
+
+ }
+
+ ArrayList resume_points;
+ int first_resume_pc;
+ public void AddResumePoint (ResumableStatement stmt, int pc)
+ {
+ if (resume_points == null) {
+ resume_points = new ArrayList ();
+ first_resume_pc = pc;
+ }
+
+ if (pc != first_resume_pc + resume_points.Count)
+ throw new InternalErrorException ("missed an intervening AddResumePoint?");
+
+ resume_points.Add (stmt);
+ }
+
+ Label dispose_try_block;
+ bool prepared_for_dispose, emitted_dispose;
+ public override Label PrepareForDispose (EmitContext ec, Label end)
+ {
+ if (!prepared_for_dispose) {
+ prepared_for_dispose = true;
+ dispose_try_block = ec.ig.DefineLabel ();
+ }
+ return dispose_try_block;
+ }
+
+ public override void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
+ {
+ if (emitted_dispose)
+ return;
+
+ emitted_dispose = true;
+
+ ILGenerator ig = ec.ig;
+
+ Label end_of_try = ig.DefineLabel ();
+
+ // Ensure that the only way we can get into this code is through a dispatcher
+ if (have_dispatcher)
+ ig.Emit (OpCodes.Br, end);
+
+ ig.BeginExceptionBlock ();
+
+ ig.MarkLabel (dispose_try_block);
+
+ Label [] labels = null;
+ for (int i = 0; i < resume_points.Count; ++i) {
+ ResumableStatement s = (ResumableStatement) resume_points [i];
+ Label ret = s.PrepareForDispose (ec, end_of_try);
+ if (ret.Equals (end_of_try) && labels == null)
+ continue;
+ if (labels == null) {
+ labels = new Label [resume_points.Count];
+ for (int j = 0; j < i; ++j)
+ labels [j] = end_of_try;
+ }
+ labels [i] = ret;
+ }
+
+ if (labels != null) {
+ int j;
+ for (j = 1; j < labels.Length; ++j)
+ if (!labels [0].Equals (labels [j]))
+ break;
+ bool emit_dispatcher = j < labels.Length;
+
+ if (emit_dispatcher) {
+ //SymbolWriter.StartIteratorDispatcher (ec.ig);
+ ig.Emit (OpCodes.Ldloc, iterator.CurrentPC);
+ IntConstant.EmitInt (ig, first_resume_pc);
+ ig.Emit (OpCodes.Sub);
+ ig.Emit (OpCodes.Switch, labels);
+ //SymbolWriter.EndIteratorDispatcher (ec.ig);
+ }
+
+ foreach (ResumableStatement s in resume_points)
+ s.EmitForDispose (ec, iterator, end_of_try, emit_dispatcher);
+ }
+
+ ig.MarkLabel (end_of_try);
+
+ ig.BeginFinallyBlock ();
+
+ EmitFinallyBody (ec);
+
+ ig.EndExceptionBlock ();