public class FlowBranchingTryCatch : FlowBranchingBlock
{
- TryCatch stmt;
+ readonly TryCatch tc;
public FlowBranchingTryCatch (FlowBranching parent, TryCatch stmt)
: base (parent, BranchingType.Block, SiblingType.Try, null, stmt.loc)
{
- this.stmt = stmt;
+ this.tc = stmt;
}
public override bool CheckRethrow (Location loc)
public override bool AddResumePoint (ResumableStatement stmt, out int pc)
{
int errors = Report.Errors;
- Parent.AddResumePoint (stmt, out pc);
+ Parent.AddResumePoint (tc.IsTryCatchFinally ? stmt : tc, out pc);
if (errors == Report.Errors) {
if (stmt is AwaitStatement) {
if (CurrentUsageVector.Next != null) {
Report.Error (1985, stmt.loc, "The `await' operator cannot be used in the body of a catch clause");
+ } else {
+ this.tc.AddResumePoint (stmt, pc);
}
} else {
if (CurrentUsageVector.Next == null)
public override bool AddBreakOrigin (UsageVector vector, Location loc)
{
Parent.AddBreakOrigin (vector, loc);
- stmt.SomeCodeFollows ();
+ tc.SomeCodeFollows ();
return true;
}
public override bool AddContinueOrigin (UsageVector vector, Location loc)
{
Parent.AddContinueOrigin (vector, loc);
- stmt.SomeCodeFollows ();
+ tc.SomeCodeFollows ();
return true;
}
public override bool AddReturnOrigin (UsageVector vector, ExitStatement exit_stmt)
{
Parent.AddReturnOrigin (vector, exit_stmt);
- stmt.SomeCodeFollows ();
+ tc.SomeCodeFollows ();
return true;
}
}
}
- public class FlowBranchingException : FlowBranching
+ public class FlowBranchingTryFinally : FlowBranching
{
ExceptionStatement stmt;
UsageVector current_vector;
SavedOrigin saved_origins;
- public FlowBranchingException (FlowBranching parent,
+ public FlowBranchingTryFinally (FlowBranching parent,
ExceptionStatement stmt)
: base (parent, BranchingType.Exception, SiblingType.Try,
null, stmt.loc)
}
}
- // Base class for statements that are implemented in terms of try...finally
- public abstract class ExceptionStatement : ResumableStatement
+ public abstract class TryFinallyBlock : ExceptionStatement
{
-#if !STATIC
- bool code_follows;
-#endif
- List<ResumableStatement> resume_points;
- int first_resume_pc;
protected Statement stmt;
Label dispose_try_block;
bool prepared_for_dispose, emitted_dispose;
- protected ExceptionStatement (Statement stmt, Location loc)
+ protected TryFinallyBlock (Statement stmt, Location loc)
+ : base (loc)
{
this.stmt = stmt;
- this.loc = loc;
}
#region Properties
#endregion
- protected abstract void EmitPreTryBody (EmitContext ec);
protected abstract void EmitTryBody (EmitContext ec);
protected abstract void EmitFinallyBody (EmitContext ec);
- protected sealed override void DoEmit (EmitContext ec)
+ public override Label PrepareForDispose (EmitContext ec, Label end)
{
- EmitPreTryBody (ec);
-
- StateMachineInitializer state_machine = null;
- if (resume_points != null) {
- state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
-
- ec.EmitInt ((int) IteratorStorey.State.Running);
- ec.Emit (OpCodes.Stloc, state_machine.CurrentPC);
- }
-
- ec.BeginExceptionBlock ();
-
- if (resume_points != null) {
- ec.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
- ec.Emit (OpCodes.Ldloc, state_machine.CurrentPC);
- ec.EmitInt (first_resume_pc);
- ec.Emit (OpCodes.Sub);
-
- Label [] labels = new Label [resume_points.Count];
- for (int i = 0; i < resume_points.Count; ++i)
- labels [i] = resume_points [i].PrepareForEmit (ec);
- ec.Emit (OpCodes.Switch, labels);
+ if (!prepared_for_dispose) {
+ prepared_for_dispose = true;
+ dispose_try_block = ec.DefineLabel ();
}
+ return dispose_try_block;
+ }
+ protected sealed override void DoEmit (EmitContext ec)
+ {
+ EmitTryBodyPrepare (ec);
EmitTryBody (ec);
ec.BeginFinallyBlock ();
Label start_finally = ec.DefineLabel ();
if (resume_points != null) {
+ var state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
+
ec.Emit (OpCodes.Ldloc, state_machine.SkipFinally);
ec.Emit (OpCodes.Brfalse_S, start_finally);
ec.Emit (OpCodes.Endfinally);
ec.EndExceptionBlock ();
}
- public void SomeCodeFollows ()
- {
-#if !STATIC
- code_follows = true;
-#endif
- }
-
- public override bool Resolve (BlockContext ec)
- {
-#if !STATIC
- // 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 ();
-#endif
- return true;
- }
-
- public void AddResumePoint (ResumableStatement stmt, int pc)
- {
- if (resume_points == null) {
- resume_points = new List<ResumableStatement> ();
- first_resume_pc = pc;
- }
-
- if (pc != first_resume_pc + resume_points.Count)
- throw new InternalErrorException ("missed an intervening AddResumePoint?");
-
- resume_points.Add (stmt);
- }
-
- public override Label PrepareForDispose (EmitContext ec, Label end)
- {
- if (!prepared_for_dispose) {
- prepared_for_dispose = true;
- dispose_try_block = ec.DefineLabel ();
- }
- return dispose_try_block;
- }
-
public override void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
{
if (emitted_dispose)
ec.MarkLabel (dispose_try_block);
- Label [] labels = null;
+ Label[] labels = null;
for (int i = 0; i < resume_points.Count; ++i) {
- ResumableStatement s = resume_points [i];
+ ResumableStatement s = 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];
+ labels = new Label[resume_points.Count];
for (int j = 0; j < i; ++j)
- labels [j] = end_of_try;
+ labels[j] = end_of_try;
}
- labels [i] = ret;
+ labels[i] = ret;
}
if (labels != null) {
int j;
for (j = 1; j < labels.Length; ++j)
- if (!labels [0].Equals (labels [j]))
+ if (!labels[0].Equals (labels[j]))
break;
bool emit_dispatcher = j < labels.Length;
}
}
- public class Lock : ExceptionStatement
+ //
+ // Base class for blocks using exception handling
+ //
+ public abstract class ExceptionStatement : ResumableStatement
+ {
+#if !STATIC
+ bool code_follows;
+#endif
+ protected List<ResumableStatement> resume_points;
+ protected int first_resume_pc;
+
+ protected ExceptionStatement (Location loc)
+ {
+ this.loc = loc;
+ }
+
+ protected virtual void EmitTryBodyPrepare (EmitContext ec)
+ {
+ StateMachineInitializer state_machine = null;
+ if (resume_points != null) {
+ state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
+
+ ec.EmitInt ((int) IteratorStorey.State.Running);
+ ec.Emit (OpCodes.Stloc, state_machine.CurrentPC);
+ }
+
+ ec.BeginExceptionBlock ();
+
+ if (resume_points != null) {
+ ec.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
+ ec.Emit (OpCodes.Ldloc, state_machine.CurrentPC);
+ ec.EmitInt (first_resume_pc);
+ ec.Emit (OpCodes.Sub);
+
+ Label[] labels = new Label[resume_points.Count];
+ for (int i = 0; i < resume_points.Count; ++i)
+ labels[i] = resume_points[i].PrepareForEmit (ec);
+ ec.Emit (OpCodes.Switch, labels);
+ }
+ }
+
+ public void SomeCodeFollows ()
+ {
+#if !STATIC
+ code_follows = true;
+#endif
+ }
+
+ public override bool Resolve (BlockContext ec)
+ {
+#if !STATIC
+ // 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 ();
+#endif
+ return true;
+ }
+
+ public void AddResumePoint (ResumableStatement stmt, int pc)
+ {
+ if (resume_points == null) {
+ resume_points = new List<ResumableStatement> ();
+ first_resume_pc = pc;
+ }
+
+ if (pc != first_resume_pc + resume_points.Count)
+ throw new InternalErrorException ("missed an intervening AddResumePoint?");
+
+ resume_points.Add (stmt);
+ }
+
+ }
+
+ public class Lock : TryFinallyBlock
{
Expression expr;
TemporaryVariableReference expr_copy;
return true;
}
- protected override void EmitPreTryBody (EmitContext ec)
+ protected override void EmitTryBodyPrepare (EmitContext ec)
{
expr_copy.EmitAssign (ec, expr);
expr_copy.Emit (ec);
ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter.Get ());
}
+
+ base.EmitTryBodyPrepare (ec);
}
protected override void EmitTryBody (EmitContext ec)
}
}
- public class TryFinally : ExceptionStatement {
+ public class TryFinally : TryFinallyBlock
+ {
Block fini;
public TryFinally (Statement stmt, Block fini, Location loc)
return ok;
}
- protected override void EmitPreTryBody (EmitContext ec)
- {
- }
-
protected override void EmitTryBody (EmitContext ec)
{
stmt.Emit (ec);
}
}
- public class TryCatch : Statement {
+ public class TryCatch : ExceptionStatement
+ {
public Block Block;
public List<Catch> Specific;
public Catch General;
- bool inside_try_finally;
-#if !STATIC
- bool code_follows;
-#endif
+ readonly bool inside_try_finally;
public TryCatch (Block block, List<Catch> catch_clauses, Location l, bool inside_try_finally)
+ : base (l)
{
this.Block = block;
this.Specific = catch_clauses;
this.General = c;
catch_clauses.RemoveAt (0);
}
+ }
- loc = l;
+ public bool IsTryCatchFinally {
+ get {
+ return inside_try_finally;
+ }
}
public override bool Resolve (BlockContext ec)
ec.EndFlowBranching ();
-#if !STATIC
- // System.Reflection.Emit automatically emits a 'leave' at the end of a try/catch clause
- // So, ensure there's some IL code after this statement
- if (!inside_try_finally && !code_follows && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
- ec.NeedReturnLabel ();
-#endif
-
- return ok;
+ return base.Resolve (ec) && ok;
}
- public void SomeCodeFollows ()
- {
-#if !STATIC
- code_follows = true;
-#endif
- }
-
- protected override void DoEmit (EmitContext ec)
+ protected sealed override void DoEmit (EmitContext ec)
{
if (!inside_try_finally)
- ec.BeginExceptionBlock ();
+ EmitTryBodyPrepare (ec);
Block.Emit (ec);
}
}
- public class Using : ExceptionStatement
+ public class Using : TryFinallyBlock
{
public class VariableDeclaration : BlockVariableDeclaration
{
#endregion
- protected override void EmitPreTryBody (EmitContext ec)
+ protected override void EmitTryBodyPrepare (EmitContext ec)
{
decl.Emit (ec);
+ base.EmitTryBodyPrepare (ec);
}
protected override void EmitTryBody (EmitContext ec)