protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
{
var res = stmt.FlowAnalysis (fc);
- parent = null;
+ parent_try_block = null;
return res;
}
{
bool ok;
- parent = bc.CurrentTryBlock;
+ parent_try_block = bc.CurrentTryBlock;
bc.CurrentTryBlock = this;
- using (bc.Set (ResolveContext.Options.TryScope)) {
+ if (stmt is TryCatch) {
ok = stmt.Resolve (bc);
+ } else {
+ using (bc.Set (ResolveContext.Options.TryScope)) {
+ ok = stmt.Resolve (bc);
+ }
}
- bc.CurrentTryBlock = parent;
+ bc.CurrentTryBlock = parent_try_block;
//
// Finally block inside iterator is called from MoveNext and
{
protected List<ResumableStatement> resume_points;
protected int first_resume_pc;
- protected ExceptionStatement parent;
+ protected ExceptionStatement parent_try_block;
+ protected int first_catch_resume_pc = -1;
protected ExceptionStatement (Location loc)
{
this.loc = loc;
}
- protected virtual void EmitBeginException (EmitContext ec)
- {
- ec.BeginExceptionBlock ();
- }
-
protected virtual void EmitTryBodyPrepare (EmitContext ec)
{
StateMachineInitializer state_machine = null;
ec.Emit (OpCodes.Stloc, state_machine.CurrentPC);
}
- EmitBeginException (ec);
+ //
+ // The resume points in catch section when this is try-catch-finally
+ //
+ if (IsRewrittenTryCatchFinally ()) {
+ ec.BeginExceptionBlock ();
+
+ if (first_catch_resume_pc >= 0) {
- if (resume_points != null) {
- ec.MarkLabel (resume_point);
+ 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 + first_catch_resume_pc);
+ ec.Emit (OpCodes.Sub);
+
+ var labels = new Label [resume_points.Count - first_catch_resume_pc];
+ for (int i = 0; i < labels.Length; ++i)
+ labels [i] = resume_points [i + first_catch_resume_pc].PrepareForEmit (ec);
+ ec.Emit (OpCodes.Switch, labels);
+ }
+ }
+
+ ec.BeginExceptionBlock ();
+
+ //
+ // The resume points for try section
+ //
+ if (resume_points != null && first_catch_resume_pc != 0) {
+ if (first_catch_resume_pc < 0)
+ 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.EmitInt (first_resume_pc);
ec.Emit (OpCodes.Sub);
- Label[] labels = new Label[resume_points.Count];
- for (int i = 0; i < resume_points.Count; ++i)
+ var labels = new Label[resume_points.Count - System.Math.Max (first_catch_resume_pc, 0)];
+ for (int i = 0; i < labels.Length; ++i)
labels[i] = resume_points[i].PrepareForEmit (ec);
ec.Emit (OpCodes.Switch, labels);
}
}
- public virtual int AddResumePoint (ResumableStatement stmt, int pc, StateMachineInitializer stateMachine)
+ bool IsRewrittenTryCatchFinally ()
{
- if (parent != null) {
- // TODO: MOVE to virtual TryCatch
- var tc = this as TryCatch;
- var s = tc != null && tc.IsTryCatchFinally ? stmt : this;
+ var tf = this as TryFinally;
+ if (tf == null)
+ return false;
+
+ var tc = tf.Statement as TryCatch;
+ if (tc == null)
+ return false;
- pc = parent.AddResumePoint (s, pc, stateMachine);
+ return tf.FinallyBlock.HasAwait || tc.HasClauseWithAwait;
+ }
+
+ public int AddResumePoint (ResumableStatement stmt, int pc, StateMachineInitializer stateMachine, TryCatch catchBlock)
+ {
+ if (parent_try_block != null) {
+ pc = parent_try_block.AddResumePoint (this, pc, stateMachine, catchBlock);
} else {
pc = stateMachine.AddResumePoint (this);
}
if (pc != first_resume_pc + resume_points.Count)
throw new InternalErrorException ("missed an intervening AddResumePoint?");
+ var tf = this as TryFinally;
+ if (tf != null && tf.Statement == catchBlock && first_catch_resume_pc < 0) {
+ first_catch_resume_pc = resume_points.Count;
+ }
+
resume_points.Add (stmt);
return pc;
}
return ok;
}
- protected override void EmitBeginException (EmitContext ec)
- {
- if (fini.HasAwait && stmt is TryCatch)
- ec.BeginExceptionBlock ();
-
- base.EmitBeginException (ec);
- }
-
protected override void EmitTryBody (EmitContext ec)
{
if (fini.HasAwait) {
ec.TryFinallyUnwind.Add (this);
stmt.Emit (ec);
- if (stmt is TryCatch)
+ if (first_catch_resume_pc < 0 && stmt is TryCatch)
ec.EndExceptionBlock ();
ec.TryFinallyUnwind.Remove (this);
}
}
+ public bool HasClauseWithAwait {
+ get {
+ return catch_sm != null;
+ }
+ }
+
public bool IsTryCatchFinally {
get {
return inside_try_finally;
bool ok;
using (bc.Set (ResolveContext.Options.TryScope)) {
- parent = bc.CurrentTryBlock;
+
+ parent_try_block = bc.CurrentTryBlock;
if (IsTryCatchFinally) {
ok = Block.Resolve (bc);
using (bc.Set (ResolveContext.Options.TryWithCatchScope)) {
bc.CurrentTryBlock = this;
ok = Block.Resolve (bc);
- bc.CurrentTryBlock = parent;
+ bc.CurrentTryBlock = parent_try_block;
}
}
}
+ var prev_catch = bc.CurrentTryCatch;
+ bc.CurrentTryCatch = this;
+
for (int i = 0; i < clauses.Count; ++i) {
var c = clauses[i];
}
}
+ bc.CurrentTryCatch = prev_catch;
+
return base.Resolve (bc) && ok;
}
}
}
- if (!inside_try_finally)
+ if (state_variable == null) {
+ if (!inside_try_finally)
+ ec.EndExceptionBlock ();
+ } else {
ec.EndExceptionBlock ();
- if (state_variable != null) {
ec.Emit (OpCodes.Ldloc, state_variable);
var labels = new Label [catch_sm.Count + 1];
}
fc.DefiniteAssignment = try_fc ?? start_fc;
- parent = null;
+ parent_try_block = null;
return res;
}