// Special case hoisted return value (happens in try/finally scenario)
//
if (ec.TryFinallyUnwind != null) {
- if (storey.HoistedReturnValue is VariableReference) {
- storey.HoistedReturnValue = ec.GetTemporaryField (storey.HoistedReturnValue.Type);
- }
-
exit_label = TryFinally.EmitRedirectedReturn (ec, async_body);
}
AwaitBlock = 1 << 13,
FinallyBlock = 1 << 14,
CatchBlock = 1 << 15,
+ HasReferenceToStoreyForInstanceLambdas = 1 << 16,
Iterator = 1 << 20,
NoFlowAnalysis = 1 << 21,
InitializationEmitted = 1 << 22
break;
}
}
-
+
//
// We are the first storey on path and 'this' has to be hoisted
//
//
// If we are state machine with no parent. We can hook into parent without additional
- // reference and capture this directly
+ // reference and capture this directly
//
ExplicitBlock parent_storey_block = pb;
while (parent_storey_block.Parent != null) {
#region Properties
+ public bool HasReferenceToStoreyForInstanceLambdas {
+ get {
+ return (flags & Flags.HasReferenceToStoreyForInstanceLambdas) != 0;
+ }
+ set {
+ flags = value ? flags | Flags.HasReferenceToStoreyForInstanceLambdas : flags & ~Flags.HasReferenceToStoreyForInstanceLambdas;
+ }
+ }
+
public bool IsAsync {
get {
return (flags & Flags.HasAsyncModifier) != 0;
continue;
}
- if (constant_label != null && constant_label != sl)
+ if (section_rc.IsUnreachable) {
+ //
+ // Common case. Previous label section end is unreachable as
+ // it ends with break, return, etc. For next section revert
+ // to reachable again unless we have constant switch block
+ //
+ section_rc = constant_label != null && constant_label != sl ?
+ Reachability.CreateUnreachable () :
+ new Reachability ();
+ } else if (prev_label != null) {
+ //
+ // Error case as control cannot fall through from one case label
+ //
+ sl.SectionStart = false;
+ s = new MissingBreak (prev_label);
+ s.MarkReachable (rc);
+ block.Statements.Insert (i - 1, s);
+ ++i;
+ } else if (constant_label != null && constant_label != sl) {
+ //
+ // Special case for the first unreachable label in constant
+ // switch block
+ //
section_rc = Reachability.CreateUnreachable ();
- else if (section_rc.IsUnreachable) {
- section_rc = new Reachability ();
- } else {
- if (prev_label != null) {
- sl.SectionStart = false;
- s = new MissingBreak (prev_label);
- s.MarkReachable (rc);
- block.Statements.Insert (i - 1, s);
- ++i;
- }
}
prev_label = sl;
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 [first_catch_resume_pc > 0 ? first_catch_resume_pc : resume_points.Count];
+ 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);
ec.Emit (OpCodes.Stloc, temp);
var exception_field = ec.GetTemporaryField (type);
+ exception_field.AutomaticallyReuse = false;
ec.EmitThis ();
ec.Emit (OpCodes.Ldloc, temp);
exception_field.EmitAssignFromStack (ec);
ec.Emit (OpCodes.Throw);
ec.MarkLabel (skip_throw);
- exception_field.IsAvailableForReuse = true;
+ exception_field.PrepareCleanup (ec);
EmitUnwindFinallyTable (ec);
}
}
}
+ 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;
}