/// <summary> /// Performs a move out operation on the goto statement. For more information see /// Chapter 2.2.1 "Outward-movement Transformations" /// </summary> /// <param name="gotoStatement"></param> /// <param name="label"></param> private void MoveOut(IfStatement gotoStatement, string label) { /// Preprocessing. BlockStatement containingBlock = gotoStatement.Parent as BlockStatement; BlockStatement outerBlock = GetOuterBlock(containingBlock); VariableReferenceExpression conditionVar = new VariableReferenceExpression(GetLabelVariable(label), null); ExtractConditionIntoVariable(conditionVar.CloneExpressionOnly() as VariableReferenceExpression, gotoStatement, containingBlock); Statement oldEnclosingStatement = containingBlock.Parent; if (oldEnclosingStatement is SwitchCase) { oldEnclosingStatement = oldEnclosingStatement.Parent; } if (containingBlock.Parent is SwitchCase || containingBlock.Parent is WhileStatement || containingBlock.Parent is DoWhileStatement || containingBlock.Parent is ForStatement || containingBlock.Parent is ForEachStatement) { /// Then we can exit using break. /// Create the if - break. BlockStatement thenBlock = new BlockStatement(); thenBlock.AddStatement(new BreakStatement(null)); //might have to keep track of brakes IfStatement breakIf = new IfStatement(conditionVar.CloneExpressionOnly(), thenBlock, null); /// Replace the original goto int gotoIndex = containingBlock.Statements.IndexOf(gotoStatement); containingBlock.Statements.Remove(gotoStatement); containingBlock.AddStatementAt(gotoIndex, breakIf); } else if (containingBlock.Parent is IfStatement || containingBlock.Parent is TryStatement || containingBlock.Parent is IfElseIfStatement) { /// Then we can exit via introducing another condition. int gotoIndex = containingBlock.Statements.IndexOf(gotoStatement); int index = gotoIndex + 1; BlockStatement thenBlock = new BlockStatement(); while (index < containingBlock.Statements.Count) { thenBlock.AddStatement(containingBlock.Statements[index]); containingBlock.Statements.RemoveAt(index); } UnaryExpression condition = new UnaryExpression(UnaryOperator.LogicalNot, conditionVar.CloneExpressionOnly(), null); IfStatement innerIf = new IfStatement(condition, thenBlock, null); /// At this point the goto statement should be the last one in the block. /// Simply replace it with the new if. containingBlock.Statements.Remove(gotoStatement); /// If the then block is empty, the if should not be added. if (innerIf.Then.Statements.Count != 0) { containingBlock.AddStatement(innerIf); } } else { throw new ArgumentOutOfRangeException("Goto statement can not leave this parent construct."); } /// Add the goto statement after the construct. /// The goto statement shoud be detached from the AST at this point. outerBlock.AddStatementAt(outerBlock.Statements.IndexOf(oldEnclosingStatement) + 1, gotoStatement); }
/// <summary> /// Performs a move-in transformation as described in Chapter 2.2.2 in <see cref="Taming Control Flow A Structured Approach to Eliminating Goto Statements.pdf"/>. /// </summary> /// <param name="gotoStatement">The goto statement to be moved in.</param> /// <param name="targetStatement">The statement that the goto will move in.</param> /// <param name="label">The target label.</param> private void MoveIn(IfStatement gotoStatement, Statement targetStatement, string label) { /// Preprocessing. BlockStatement containingBlock = gotoStatement.Parent as BlockStatement; VariableReferenceExpression conditionVar = new VariableReferenceExpression(GetLabelVariable(label), null); /// Create the statement conditionVariable = originalCondition; /// Add the assigning statement before gotoStatement and replace originalCondition with conditionVariable ExtractConditionIntoVariable(conditionVar.CloneExpressionOnly() as VariableReferenceExpression, gotoStatement, containingBlock); /// Collect the statements between the goto jump and the target statement. int gotoIndex = containingBlock.Statements.IndexOf(gotoStatement); int targetIndex = containingBlock.Statements.IndexOf(targetStatement); BlockStatement thenBlock = CollectStatements(gotoIndex + 1, targetIndex, containingBlock); /// Create the new if. IfStatement outerIfStatement = new IfStatement(new UnaryExpression(UnaryOperator.LogicalNot, conditionVar.CloneExpressionOnly(), null), thenBlock, null); /// Replace the old goto with the new if statement. if (outerIfStatement.Then.Statements.Count > 0) { containingBlock.AddStatementAt(gotoIndex, outerIfStatement); } containingBlock.Statements.Remove(gotoStatement); /// At this point the original goto statement is completely detached from the AST. It must be reattached as the first statement in the target. if (targetStatement is DoWhileStatement) { /// Loops with more than one entry can be created only by this step, so the do-while loops is the only one /// that must be considered. (targetStatement as DoWhileStatement).Body.AddStatementAt(0, gotoStatement); } else if (targetStatement is IfStatement) { /// If statements with more than one entry to then/else blocks can be created only by this step, so that's why /// the only case that must be considered is the case when the target is in the then block. IfStatement targetIf = targetStatement as IfStatement; targetIf.Condition = UpdateCondition(targetIf.Condition, conditionVar.CloneExpressionOnly() as VariableReferenceExpression); /// This greatly depends on the short-circut evaluation. targetIf.Then.AddStatementAt(0, gotoStatement); } else if (targetStatement is SwitchCase) { MoveInCase(gotoStatement, targetStatement as SwitchCase, label); } else if (targetStatement is WhileStatement) { /// This case should not be reachable. WhileStatement @while = targetStatement as WhileStatement; @while.Body.AddStatementAt(0, gotoStatement); @while.Condition = UpdateCondition(@while.Condition, conditionVar.CloneExpressionOnly() as VariableReferenceExpression); } else { throw new NotSupportedException("Unsupported target statement for goto jump."); } }
/// <summary> /// Contains the logic for moving a goto inside the case of switch statement. For more information /// see Chapter "2.2.2 Inward-movement Transformations : Moving a goto into a switch statement" from /// <see cref="Taming Control Flow A Structured Approach to Eliminating Goto Statements.pdf"/>. /// </summary> /// <param name="gotoStatement">The goto statement to be moved in.</param> /// <param name="switchCase">The switch case that contains the target of the goto statement.</param> /// <param name="label">The label of the target.</param> private void MoveInCase(IfStatement gotoStatement, SwitchCase switchCase, string label) { VariableReferenceExpression conditionVariable = new VariableReferenceExpression(GetLabelVariable(label), null); BlockStatement containingBlock = GetOuterBlock(gotoStatement); SwitchStatement switchStatement = switchCase.Parent as SwitchStatement; int gotoIndex = containingBlock.Statements.IndexOf(gotoStatement); int switchIndex = containingBlock.Statements.IndexOf(switchStatement); /// Generate variable and extract the switch condition string switchVariableName = "switch" + switchStatement.ConditionBlock.First.Offset; TypeReference switchVariableType = GetSwitchType(switchStatement); VariableDefinition switchVariable = new VariableDefinition(switchVariableName, switchVariableType); switchVariables.Add(switchVariable); VariableReferenceExpression switchVarEx = new VariableReferenceExpression(switchVariable, null); ExtractConditionIntoVariable(switchVarEx, switchStatement, containingBlock); BlockStatement thenBlock = CollectStatements(gotoIndex + 1, switchIndex + 1, containingBlock); BlockStatement elseBlock = new BlockStatement(); /// Adds swCond = caseCond; in the last part of else. BinaryExpression assignSwitchVariable = new BinaryExpression(BinaryOperator.Assign, switchVarEx.CloneExpressionOnly(), GetCaseConditionExpression(switchCase), typeSystem, null); elseBlock.AddStatement(new ExpressionStatement(assignSwitchVariable)); IfStatement precedingIf = new IfStatement(new UnaryExpression(UnaryOperator.LogicalNot, conditionVariable, null), thenBlock, elseBlock); /// Attach new if and move the goto conditional. /// Attach it only if the then block is not empty. if (precedingIf.Then.Statements.Count != 0) { containingBlock.AddStatementAt(gotoIndex, precedingIf); } containingBlock.Statements.Remove(gotoStatement); switchCase.Body.AddStatementAt(0, gotoStatement); }
/// <summary> /// Extracts the condition of the condition statement into new variable. Replaces the condition of the statement with the new variable and inserts /// the assignment statement right before the condition statement. /// </summary> /// <param name="conditionVar">The variable which should be assigned the condition.</param> /// <param name="gotoStatement">The conditional goto.</param> /// <param name="containingBlock">The block that contains the conditional goto.</param> private void ExtractConditionIntoVariable(VariableReferenceExpression conditionVar, ConditionStatement statement, BlockStatement containingBlock) { /// Create the statement conditionVariable = originalCondition; BinaryExpression conditionAssignment = new BinaryExpression(BinaryOperator.Assign, conditionVar, statement.Condition, typeSystem, null); ExpressionStatement assignStatement = new ExpressionStatement(conditionAssignment); /// Insert the statement before the original goto statement. int gotoIndex = containingBlock.Statements.IndexOf(statement); containingBlock.AddStatementAt(gotoIndex, assignStatement); /// Update the condition of the goto statement. statement.Condition = conditionVar.CloneExpressionOnly(); }