/// <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); // Construct a loop expression. // var enumerator = TypeUtilities.EnumeratePropertyNames(rhs).GetEnumerator(); // while (true) { // continue-target: // if (enumerator.MoveNext() == false) // goto break-target; // lhs = enumerator.Current; // // <body statements> // } // break-target: // Call IEnumerable<string> EnumeratePropertyNames(ScriptEngine engine, object obj) EmitHelpers.LoadScriptEngine(generator); this.TargetObject.GenerateCode(generator, optimizationInfo); EmitConversion.ToAny(generator, this.TargetObject.ResultType); generator.Call(ReflectionHelpers.TypeUtilities_EnumeratePropertyNames); // Call IEnumerable<string>.GetEnumerator() generator.Call(ReflectionHelpers.IEnumerable_GetEnumerator); // Store the enumerator in a temporary variable. var enumerator = generator.CreateTemporaryVariable(typeof(IEnumerator<string>)); generator.StoreVariable(enumerator); var breakTarget = generator.CreateLabel(); var continueTarget = generator.DefineLabelPosition(); // Emit debugging information. if (optimizationInfo.DebugDocument != null) generator.MarkSequencePoint(optimizationInfo.DebugDocument, this.VariableDebugInfo); // if (enumerator.MoveNext() == false) // goto break-target; generator.LoadVariable(enumerator); generator.Call(ReflectionHelpers.IEnumerator_MoveNext); generator.BranchIfFalse(breakTarget); // lhs = enumerator.Current; generator.LoadVariable(enumerator); generator.Call(ReflectionHelpers.IEnumerator_Current); this.Variable.GenerateSet(generator, optimizationInfo, PrimitiveType.String, false); // Emit the body statement(s). optimizationInfo.PushBreakOrContinueInfo(this.Labels, breakTarget, continueTarget, labelledOnly: false); this.Body.GenerateCode(generator, optimizationInfo); optimizationInfo.PopBreakOrContinueInfo(); generator.Branch(continueTarget); generator.DefineLabelPosition(breakTarget); // Generate code for the end of the statement. GenerateEndOfStatement(generator, optimizationInfo, statementLocals); }
/// <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(); GenerateStartOfStatement(generator, optimizationInfo, statementLocals); if (this.ContributesToEvalResult == true && optimizationInfo.EvalResult != null) { // Emit the expression. this.Expression.GenerateCode(generator, optimizationInfo); // Store the result. EmitConversion.ToAny(generator, this.Expression.ResultType); generator.StoreVariable(optimizationInfo.EvalResult); } else { // Emit the expression. this.Expression.GenerateCode(generator, optimizationInfo); generator.Pop(); } // Generate code for the end of the statement. GenerateEndOfStatement(generator, optimizationInfo, statementLocals); }
/// <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(); GenerateStartOfStatement(generator, optimizationInfo, statementLocals); // Create the scope. this.Scope.GenerateScopeCreation(generator, optimizationInfo); // Make sure the scope is reverted even if an exception is thrown. generator.BeginExceptionBlock(); // Setting the InsideTryCatchOrFinally flag converts BR instructions into LEAVE // instructions so that the finally block is executed correctly. var previousInsideTryCatchOrFinally = optimizationInfo.InsideTryCatchOrFinally; optimizationInfo.InsideTryCatchOrFinally = true; // Generate code for the body statements. this.Body.GenerateCode(generator, optimizationInfo); // Reset the InsideTryCatchOrFinally flag. optimizationInfo.InsideTryCatchOrFinally = previousInsideTryCatchOrFinally; // Revert the scope. generator.BeginFinallyBlock(); this.Scope.GenerateScopeDestruction(generator, optimizationInfo); generator.EndExceptionBlock(); // Generate code for the end of the statement. GenerateEndOfStatement(generator, optimizationInfo, statementLocals); }
/// <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(); GenerateStartOfStatement(generator, optimizationInfo, statementLocals); // Generate code for the end of the statement. GenerateEndOfStatement(generator, optimizationInfo, statementLocals); }
/// <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() { NonDefaultSourceSpanBehavior = true }; GenerateStartOfStatement(generator, optimizationInfo, statementLocals); // Generate code for the end of the statement. GenerateEndOfStatement(generator, optimizationInfo, statementLocals); }
/// <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(); GenerateStartOfStatement(generator, optimizationInfo, statementLocals); // Emit an unconditional branch. // Note: the continue statement might be branching from inside a try { } or finally { } // block to outside. EmitLongJump() handles this. optimizationInfo.EmitLongJump(generator, optimizationInfo.GetContinueTarget(this.Label)); // Generate code for the end of the statement. GenerateEndOfStatement(generator, optimizationInfo, statementLocals); }
/// <summary> /// Generates CIL for the end of every 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> /// <param name="locals"> Variables common to both GenerateStartOfStatement() and GenerateEndOfStatement(). </param> public void GenerateEndOfStatement(ILGenerator generator, OptimizationInfo optimizationInfo, StatementLocals locals) { if (locals.NonDefaultBreakStatementBehavior == false && this.HasLabels == true) { // Revert the information needed by the break statement. generator.DefineLabelPosition(locals.EndOfStatement); optimizationInfo.PopBreakOrContinueInfo(); } #if DEBUG && !SILVERLIGHT && !XBOX // Check that the stack count is zero. if (generator is DynamicILGenerator && ((DynamicILGenerator)generator).StackSize != locals.OriginalStackSize) throw new InvalidOperationException("Encountered unexpected stack imbalance."); #endif }
/// <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(); GenerateStartOfStatement(generator, optimizationInfo, statementLocals); // Inserts a breakpoint into the IL. generator.Breakpoint(); // When the debugger stops, it stops at the first instruction after the breakpoint. By // inserting a no-op operation the debugger will highlight the "debugger" statement // instead of the statement after the "debugger" statement. generator.NoOperation(); // Generate code for the end of the statement. GenerateEndOfStatement(generator, optimizationInfo, statementLocals); }
/// <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(); GenerateStartOfStatement(generator, optimizationInfo, statementLocals); // Emit code to throw the given value. this.Value.GenerateCode(generator, optimizationInfo); EmitConversion.ToAny(generator, this.Value.ResultType); generator.LoadInt32(0); generator.LoadNull(); generator.NewObject(ReflectionHelpers.JavaScriptException_Constructor_Object); generator.Throw(); // Generate code for the end of the statement. GenerateEndOfStatement(generator, optimizationInfo, statementLocals); }
/// <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(); GenerateStartOfStatement(generator, optimizationInfo, statementLocals); // Emit the return value. if (this.Value == null) EmitHelpers.EmitUndefined(generator); else { this.Value.GenerateCode(generator, optimizationInfo); EmitConversion.ToAny(generator, this.Value.ResultType); } // Determine if this is the last statement in the function. bool lastStatement = optimizationInfo.AbstractSyntaxTree is BlockStatement && ((BlockStatement)optimizationInfo.AbstractSyntaxTree).Statements.Count > 0 && ((BlockStatement)optimizationInfo.AbstractSyntaxTree).Statements[((BlockStatement)optimizationInfo.AbstractSyntaxTree).Statements.Count - 1] == this; // The first return statement initializes the variable that holds the return value. if (optimizationInfo.ReturnVariable == null) optimizationInfo.ReturnVariable = generator.DeclareVariable(typeof(object), "returnValue"); // Store the return value in a variable. generator.StoreVariable(optimizationInfo.ReturnVariable); // There is no need to jump to the end of the function if this is the last statement. if (lastStatement == false) { // The first return statement that needs to branch creates the return label. This is // defined in FunctionmethodGenerator.GenerateCode() at the end of the function. if (optimizationInfo.ReturnTarget == null) optimizationInfo.ReturnTarget = generator.CreateLabel(); // Branch to the end of the function. Note: the return statement might be branching // from inside a try { } or finally { } block to outside. EmitLongJump() handles this. optimizationInfo.EmitLongJump(generator, optimizationInfo.ReturnTarget); } // Generate code for the end of the statement. GenerateEndOfStatement(generator, optimizationInfo, statementLocals); }
/// <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(); GenerateStartOfStatement(generator, optimizationInfo, statementLocals); // Generate code for the condition and coerce to a boolean. this.Condition.GenerateCode(generator, optimizationInfo); EmitConversion.ToBool(generator, this.Condition.ResultType); // We will need a label at the end of the if statement. var endOfEverything = generator.CreateLabel(); if (this.ElseClause == null) { // Jump to the end if the condition is false. generator.BranchIfFalse(endOfEverything); // Generate code for the if clause. this.IfClause.GenerateCode(generator, optimizationInfo); } else { // Branch to the else clause if the condition is false. var startOfElseClause = generator.CreateLabel(); generator.BranchIfFalse(startOfElseClause); // Generate code for the if clause. this.IfClause.GenerateCode(generator, optimizationInfo); // Branch to the end of the if statement. generator.Branch(endOfEverything); // Generate code for the else clause. generator.DefineLabelPosition(startOfElseClause); this.ElseClause.GenerateCode(generator, optimizationInfo); } // Define the label at the end of the if statement. generator.DefineLabelPosition(endOfEverything); // Generate code for the end of the statement. GenerateEndOfStatement(generator, optimizationInfo, statementLocals); }
/// <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() { NonDefaultSourceSpanBehavior = true }; GenerateStartOfStatement(generator, optimizationInfo, statementLocals); foreach (var declaration in this.Declarations) { if (declaration.InitExpression != null) { // Create a new assignment expression and generate code for it. var initializationStatement = new ExpressionStatement( new AssignmentExpression(this.Scope, declaration.VariableName, declaration.InitExpression)); initializationStatement.SourceSpan = declaration.SourceSpan; initializationStatement.GenerateCode(generator, optimizationInfo); } } // Generate code for the end of the statement. GenerateEndOfStatement(generator, optimizationInfo, statementLocals); }
/// <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(); GenerateStartOfStatement(generator, optimizationInfo, statementLocals); foreach (var declaration in this.Declarations) { if (declaration.InitExpression != null) { // Create a new assignment expression and generate code for it. if (optimizationInfo.DebugDocument != null) generator.MarkSequencePoint(optimizationInfo.DebugDocument, declaration.DebugInfo); var initializationStatement = new ExpressionStatement( new AssignmentExpression(this.Scope, declaration.VariableName, declaration.InitExpression)); initializationStatement.GenerateCode(generator, optimizationInfo); } } // Generate code for the end of the statement. GenerateEndOfStatement(generator, optimizationInfo, statementLocals); }
/// <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 }; GenerateStartOfStatement(generator, optimizationInfo, statementLocals); // We need a label for each case clause and one for the default case. var jumpTargets = new ILLabel[this.CaseClauses.Count]; int defaultIndex = -1; ILLabel endOfSwitch = generator.CreateLabel(); // Generate code for the switch value. var startOfSwitch = generator.CreateLabel(); this.Value.GenerateCode(generator, optimizationInfo); EmitConversion.ToAny(generator, this.Value.ResultType); // Save the switch value in a variable. var switchValue = generator.CreateTemporaryVariable(typeof(object)); generator.StoreVariable(switchValue); for (int i = 0; i < this.CaseClauses.Count; i ++) { var caseClause = this.CaseClauses[i]; // Create a label for each clause. jumpTargets[i] = generator.CreateLabel(); if (caseClause.Value == null) { // This is a default clause. defaultIndex = i; continue; } // TypeComparer.StrictEquals(switchValue, caseValue) generator.LoadVariable(switchValue); caseClause.Value.GenerateCode(generator, optimizationInfo); EmitConversion.ToAny(generator, caseClause.Value.ResultType); generator.Call(ReflectionHelpers.TypeComparer_StrictEquals); // if (TypeComparer.StrictEquals(switchValue, caseValue) == true) // goto case i generator.BranchIfTrue(jumpTargets[i]); } // None of the cases matched, jump to the default clause or the end of the switch. if (defaultIndex >= 0) generator.Branch(jumpTargets[defaultIndex]); else generator.Branch(endOfSwitch); for (int i = 0; i < this.CaseClauses.Count; i++) { // Define a label at the start of the case clause. generator.DefineLabelPosition(jumpTargets[i]); // Set up the information needed by the break statement. optimizationInfo.PushBreakOrContinueInfo(this.Labels, endOfSwitch, null, labelledOnly: false); // Emit the case clause statements. foreach (var statement in this.CaseClauses[i].BodyStatements) statement.GenerateCode(generator, optimizationInfo); // Revert the information needed by the break statement. optimizationInfo.PopBreakOrContinueInfo(); } // Define a label for the end of the switch statement. generator.DefineLabelPosition(endOfSwitch); // Release the switch value variable for use elsewhere. generator.ReleaseTemporaryVariable(switchValue); // Generate code for the end of the statement. GenerateEndOfStatement(generator, optimizationInfo, statementLocals); }
/// <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, NonDefaultSourceSpanBehavior = true }; GenerateStartOfStatement(generator, optimizationInfo, statementLocals); // Construct a loop expression. // var iterator = TypeUtilities.GetIterator(obj); // while (true) { // continue-target: // if (enumerator.MoveNext() == false) // goto break-target; // lhs = enumerator.Current; // // <body statements> // } // break-target: // Call: ObjectInstance GetIterator(ScriptEngine engine, ObjectInstance iterable) // Then call: IEnumerable<object> Iterate(ScriptEngine engine, ObjectInstance iterator) optimizationInfo.MarkSequencePoint(generator, this.TargetObjectSourceSpan); EmitHelpers.LoadScriptEngine(generator); this.TargetObject.GenerateCode(generator, optimizationInfo); EmitConversion.ToAny(generator, this.TargetObject.ResultType); generator.Call(ReflectionHelpers.TypeUtilities_ForOf); // Call IEnumerable<object>.GetEnumerator() generator.Call(ReflectionHelpers.IEnumerable_Object_GetEnumerator); // Store the enumerator in a temporary variable. var enumerator = generator.CreateTemporaryVariable(typeof(IEnumerator <object>)); generator.StoreVariable(enumerator); var breakTarget = generator.CreateLabel(); var continueTarget = generator.DefineLabelPosition(); // Generate the scope variable if necessary. this.Scope.GenerateScopeCreation(generator, optimizationInfo); // Emit debugging information. if (optimizationInfo.DebugDocument != null) { generator.MarkSequencePoint(optimizationInfo.DebugDocument, this.VariableSourceSpan); } // if (enumerator.MoveNext() == false) // goto break-target; generator.LoadVariable(enumerator); generator.Call(ReflectionHelpers.IEnumerator_MoveNext); generator.BranchIfFalse(breakTarget); // lhs = enumerator.Current; this.Variable.GenerateReference(generator, optimizationInfo); generator.LoadVariable(enumerator); generator.Call(ReflectionHelpers.IEnumerator_Object_Current); this.Variable.GenerateSet(generator, optimizationInfo, PrimitiveType.Any); // Emit the body statement(s). optimizationInfo.PushBreakOrContinueInfo(this.Labels, breakTarget, continueTarget, labelledOnly: false); this.Body.GenerateCode(generator, optimizationInfo); optimizationInfo.PopBreakOrContinueInfo(); generator.Branch(continueTarget); generator.DefineLabelPosition(breakTarget); // Generate code for the end of the statement. GenerateEndOfStatement(generator, optimizationInfo, statementLocals); }
/// <summary> /// Generates CIL for the start of every 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> /// <param name="locals"> Variables common to both GenerateStartOfStatement() and GenerateEndOfStatement(). </param> public void GenerateStartOfStatement(ILGenerator generator, OptimizationInfo optimizationInfo, StatementLocals locals) { #if DEBUG && !SILVERLIGHT // Statements must not produce or consume any values on the stack. if (generator is DynamicILGenerator) locals.OriginalStackSize = ((DynamicILGenerator)generator).StackSize; #endif if (locals.NonDefaultBreakStatementBehavior == false && this.HasLabels == true) { // Set up the information needed by the break statement. locals.EndOfStatement = generator.CreateLabel(); optimizationInfo.PushBreakOrContinueInfo(this.Labels, locals.EndOfStatement, null, labelledOnly: true); } // Emit debugging information. if (locals.NonDefaultDebugInfoBehavior == false && optimizationInfo.DebugDocument != null && this.DebugInfo != null) generator.MarkSequencePoint(optimizationInfo.DebugDocument, this.DebugInfo); }
/// <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() { NonDefaultSourceSpanBehavior = true }; GenerateStartOfStatement(generator, optimizationInfo, statementLocals); // Unlike in .NET, in javascript there are no restrictions on what can appear inside // try, catch and finally blocks. The one restriction which causes problems is the // inability to jump out of .NET finally blocks. This is required when break, continue // or return statements appear inside of a finally block. To work around this, when // inside a finally block these instructions throw an exception instead. // Setting the InsideTryCatchOrFinally flag converts BR instructions into LEAVE // instructions so that the finally block is executed correctly. var previousInsideTryCatchOrFinally = optimizationInfo.InsideTryCatchOrFinally; optimizationInfo.InsideTryCatchOrFinally = true; // Finally requires two exception nested blocks. if (this.FinallyBlock != null) generator.BeginExceptionBlock(); // Begin the exception block. generator.BeginExceptionBlock(); // Generate code for the try block. this.TryBlock.GenerateCode(generator, optimizationInfo); // Generate code for the catch block. if (this.CatchBlock != null) { // Begin a catch block. The exception is on the top of the stack. generator.BeginCatchBlock(typeof(JavaScriptException)); // Create a new DeclarativeScope. this.CatchScope.GenerateScopeCreation(generator, optimizationInfo); // Store the error object in the variable provided. generator.Call(ReflectionHelpers.JavaScriptException_ErrorObject); var catchVariable = new NameExpression(this.CatchScope, this.CatchVariableName); catchVariable.GenerateSet(generator, optimizationInfo, PrimitiveType.Any, false); // Make sure the scope is reverted even if an exception is thrown. generator.BeginExceptionBlock(); // Emit code for the statements within the catch block. this.CatchBlock.GenerateCode(generator, optimizationInfo); // Revert the scope. generator.BeginFinallyBlock(); this.CatchScope.GenerateScopeDestruction(generator, optimizationInfo); generator.EndExceptionBlock(); } // Generate code for the finally block. if (this.FinallyBlock != null) { generator.BeginFinallyBlock(); var branches = new List<ILLabel>(); var previousStackSize = optimizationInfo.LongJumpStackSizeThreshold; optimizationInfo.LongJumpStackSizeThreshold = optimizationInfo.BreakOrContinueStackSize; var previousCallback = optimizationInfo.LongJumpCallback; optimizationInfo.LongJumpCallback = (generator2, label) => { // It is not possible to branch out of a finally block - therefore instead of // generating LEAVE instructions we throw an exception then catch it to transfer // control out of the finally block. generator2.LoadInt32(branches.Count); generator2.NewObject(ReflectionHelpers.LongJumpException_Constructor); generator2.Throw(); // Record any branches that are made within the finally code. branches.Add(label); }; // Emit code for the finally block. this.FinallyBlock.GenerateCode(generator, optimizationInfo); // End the main exception block. generator.EndExceptionBlock(); // Begin a catch block to catch any LongJumpExceptions. The exception object is on // the top of the stack. generator.BeginCatchBlock(typeof(LongJumpException)); if (branches.Count > 0) { // switch (exception.RouteID) // { // case 0: goto label1; // case 1: goto label2; // } ILLabel[] switchLabels = new ILLabel[branches.Count]; for (int i = 0; i < branches.Count; i++) switchLabels[i] = generator.CreateLabel(); generator.Call(ReflectionHelpers.LongJumpException_RouteID); generator.Switch(switchLabels); for (int i = 0; i < branches.Count; i++) { generator.DefineLabelPosition(switchLabels[i]); generator.Leave(branches[i]); } } // Reset the state we clobbered. optimizationInfo.LongJumpStackSizeThreshold = previousStackSize; optimizationInfo.LongJumpCallback = previousCallback; } // End the exception block. generator.EndExceptionBlock(); // Reset the InsideTryCatchOrFinally flag. optimizationInfo.InsideTryCatchOrFinally = previousInsideTryCatchOrFinally; // Generate code for the end of the statement. GenerateEndOfStatement(generator, optimizationInfo, statementLocals); }
/// <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, NonDefaultSourceSpanBehavior = 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) { optimizationInfo.MarkSequencePoint(generator, this.ConditionStatement.SourceSpan); 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) { optimizationInfo.MarkSequencePoint(generator, this.ConditionStatement.SourceSpan); 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); }