/// <summary>
/// Generates CIL for the statement.
/// </summary>
/// <param name="generator"> The generator to output the CIL to. </param>
/// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param>
public override void GenerateCode(ILGenerator generator, OptimizationInfo optimizationInfo)
{
// Generate code for the start of the statement.
var statementLocals = new StatementLocals()
{
NonDefaultBreakStatementBehavior = true, NonDefaultDebugInfoBehavior = true
};
GenerateStartOfStatement(generator, optimizationInfo, statementLocals);
// <initializer>
// if (<condition>)
// {
// <loop body>
// <increment>
// while (true) {
// if (<condition> == false)
// break;
//
// <body statements>
//
// continue-target:
// <increment>
// }
// }
// break-target:
// Set up some labels.
var continueTarget = generator.CreateLabel();
var breakTarget1 = generator.CreateLabel();
var breakTarget2 = generator.CreateLabel();
// Emit the initialization statement.
if (this.InitStatement != null)
{
this.InitStatement.GenerateCode(generator, optimizationInfo);
}
// Check the condition and jump to the end if it is false.
if (this.CheckConditionAtEnd == false && this.ConditionStatement != null)
{
#if !XBOX
if (optimizationInfo.DebugDocument != null)
{
generator.MarkSequencePoint(optimizationInfo.DebugDocument, this.ConditionStatement.DebugInfo);
}
#endif
this.Condition.GenerateCode(generator, optimizationInfo);
EmitConversion.ToBool(generator, this.Condition.ResultType);
generator.BranchIfFalse(breakTarget1);
}
// Emit the loop body.
optimizationInfo.PushBreakOrContinueInfo(this.Labels, breakTarget1, continueTarget, false);
this.Body.GenerateCode(generator, optimizationInfo);
optimizationInfo.PopBreakOrContinueInfo();
// Increment the loop variable.
if (this.IncrementStatement != null)
{
this.IncrementStatement.GenerateCode(generator, optimizationInfo);
}
// Strengthen the variable types.
List <KeyValuePair <Scope.DeclaredVariable, RevertInfo> > previousVariableTypes = null;
var previousInsideTryCatchOrFinally = optimizationInfo.InsideTryCatchOrFinally;
if (optimizationInfo.OptimizeInferredTypes == true)
{
// Keep a record of the variable types before strengthening.
previousVariableTypes = new List <KeyValuePair <Scope.DeclaredVariable, RevertInfo> >();
var typedVariables = FindTypedVariables();
foreach (var variableAndType in typedVariables)
{
var variable = variableAndType.Key;
var variableInfo = variableAndType.Value;
if (variableInfo.Conditional == false && variableInfo.Type != variable.Type)
{
// Save the previous type so we can restore it later.
var previousType = variable.Type;
previousVariableTypes.Add(new KeyValuePair <Scope.DeclaredVariable, RevertInfo>(variable, new RevertInfo()
{
Type = previousType, Variable = variable.Store
}));
// Load the existing value.
var nameExpression = new NameExpression(variable.Scope, variable.Name);
nameExpression.GenerateGet(generator, optimizationInfo, false);
// Store the typed value.
variable.Store = generator.DeclareVariable(variableInfo.Type);
variable.Type = variableInfo.Type;
nameExpression.GenerateSet(generator, optimizationInfo, previousType, false);
}
}
// The variables must be reverted even in the presence of exceptions.
if (previousVariableTypes.Count > 0)
{
generator.BeginExceptionBlock();
// Setting the InsideTryCatchOrFinally flag converts BR instructions into LEAVE
// instructions so that the finally block is executed correctly.
optimizationInfo.InsideTryCatchOrFinally = true;
}
}
// The inner loop starts here.
var startOfLoop = generator.DefineLabelPosition();
// Check the condition and jump to the end if it is false.
if (this.ConditionStatement != null)
{
#if !XBOX
if (optimizationInfo.DebugDocument != null)
{
generator.MarkSequencePoint(optimizationInfo.DebugDocument, this.ConditionStatement.DebugInfo);
}
#endif
this.Condition.GenerateCode(generator, optimizationInfo);
EmitConversion.ToBool(generator, this.Condition.ResultType);
generator.BranchIfFalse(breakTarget2);
}
// Emit the loop body.
optimizationInfo.PushBreakOrContinueInfo(this.Labels, breakTarget2, continueTarget, labelledOnly: false);
this.Body.GenerateCode(generator, optimizationInfo);
optimizationInfo.PopBreakOrContinueInfo();
// The continue statement jumps here.
generator.DefineLabelPosition(continueTarget);
// Increment the loop variable.
if (this.IncrementStatement != null)
{
this.IncrementStatement.GenerateCode(generator, optimizationInfo);
}
// Unconditionally branch back to the start of the loop.
generator.Branch(startOfLoop);
// Define the end of the loop (actually just after).
generator.DefineLabelPosition(breakTarget2);
// Revert the variable types.
if (previousVariableTypes != null && previousVariableTypes.Count > 0)
{
// Revert the InsideTryCatchOrFinally flag.
optimizationInfo.InsideTryCatchOrFinally = previousInsideTryCatchOrFinally;
// Revert the variable types within a finally block.
generator.BeginFinallyBlock();
foreach (var previousVariableAndType in previousVariableTypes)
{
var variable = previousVariableAndType.Key;
var variableRevertInfo = previousVariableAndType.Value;
// Load the existing value.
var nameExpression = new NameExpression(variable.Scope, variable.Name);
nameExpression.GenerateGet(generator, optimizationInfo, false);
// Store the typed value.
var previousType = variable.Type;
variable.Store = variableRevertInfo.Variable;
variable.Type = variableRevertInfo.Type;
nameExpression.GenerateSet(generator, optimizationInfo, previousType, false);
}
// End the exception block.
generator.EndExceptionBlock();
}
// Define the end of the loop (actually just after).
generator.DefineLabelPosition(breakTarget1);
// Generate code for the end of the statement.
GenerateEndOfStatement(generator, optimizationInfo, statementLocals);
}