private BoundStatement RewriteWhileStatement( BoundLoopStatement loop, BoundExpression rewrittenCondition, BoundStatement rewrittenBody, GeneratedLabelSymbol breakLabel, GeneratedLabelSymbol continueLabel, bool hasErrors) { Debug.Assert(loop.Kind == BoundKind.WhileStatement || loop.Kind == BoundKind.ForEachStatement); // while (condition) // body; // // becomes // // goto continue; // start: // { // body // continue: // GotoIfTrue condition start; // } // break: SyntaxNode syntax = loop.Syntax; var startLabel = new GeneratedLabelSymbol("start"); BoundStatement ifConditionGotoStart = new BoundConditionalGoto(rewrittenCondition.Syntax, rewrittenCondition, true, startLabel); BoundStatement gotoContinue = new BoundGotoStatement(syntax, continueLabel); if (this.Instrument && !loop.WasCompilerGenerated) { switch (loop.Kind) { case BoundKind.WhileStatement: ifConditionGotoStart = _instrumenter.InstrumentWhileStatementConditionalGotoStartOrBreak((BoundWhileStatement)loop, ifConditionGotoStart); break; case BoundKind.ForEachStatement: ifConditionGotoStart = _instrumenter.InstrumentForEachStatementConditionalGotoStart((BoundForEachStatement)loop, ifConditionGotoStart); break; default: throw ExceptionUtilities.UnexpectedValue(loop.Kind); } // mark the initial jump as hidden. We do it to tell that this is not a part of previous statement. This // jump may be a target of another jump (for example if loops are nested) and that would give the // impression that the previous statement is being re-executed. gotoContinue = new BoundSequencePoint(null, gotoContinue); } return BoundStatementList.Synthesized(syntax, hasErrors, gotoContinue, new BoundLabelStatement(syntax, startLabel), rewrittenBody, new BoundLabelStatement(syntax, continueLabel), ifConditionGotoStart, new BoundLabelStatement(syntax, breakLabel)); }
public override BoundNode VisitGotoStatement(BoundGotoStatement node) { BoundExpression caseExpressionOpt = (BoundExpression)this.Visit(node.CaseExpressionOpt); BoundLabel labelExpressionOpt = (BoundLabel)this.Visit(node.LabelExpressionOpt); var proxyLabel = _currentAwaitFinallyFrame.ProxyLabelIfNeeded(node.Label); return(node.Update(proxyLabel, caseExpressionOpt, labelExpressionOpt)); }
public override BoundNode VisitGotoStatement(BoundGotoStatement node) { BoundExpression caseExpressionOpt = (BoundExpression)this.Visit(node.CaseExpressionOpt); BoundLabel labelExpressionOpt = (BoundLabel)this.Visit(node.LabelExpressionOpt); var proxyLabel = _currentFinallyFrame.ProxyLabelIfNeeded(node.Label); Debug.Assert(node.Label == proxyLabel || !(F.CurrentFunction is IteratorFinallyMethodSymbol), "should not be proxying branches in finally"); return(node.Update(proxyLabel, caseExpressionOpt, labelExpressionOpt)); }
public override BoundNode VisitContinueStatement(BoundContinueStatement node) { BoundStatement result = new BoundGotoStatement(node.Syntax, node.Label, node.HasErrors); if (this.Instrument && !node.WasCompilerGenerated) { result = _instrumenter.InstrumentContinueStatement(node, result); } return result; }
public override BoundNode VisitBreakStatement(BoundBreakStatement node) { BoundStatement result = new BoundGotoStatement(node.Syntax, node.Label, node.HasErrors); if (this.Instrument && !node.WasCompilerGenerated) { result = _instrumenter.InstrumentBreakStatement(node, result); } return(result); }
private LocalState VisitSwitchHeader(BoundSwitchStatement node) { // Initial value for the Break state for a switch statement is established as follows: // Break state = UnreachableState if either of the following is true: // (1) there is a default label, or // (2) the switch expression is constant and there is a matching case label. // Otherwise, the Break state = current state. // visit switch expression VisitRvalue(node.Expression); LocalState breakState = this.State; // For a switch statement, we simulate a possible jump to the switch labels to ensure that // the label is not treated as an unused label and a pending branch to the label is noted. // However, if switch expression is a constant, we must have determined the single target label // at bind time, i.e. node.ConstantTargetOpt, and we must simulate a jump only to this label. var constantTargetOpt = node.ConstantTargetOpt; if ((object)constantTargetOpt == null) { bool hasDefaultLabel = false; foreach (var section in node.SwitchSections) { foreach (var boundSwitchLabel in section.SwitchLabels) { var label = boundSwitchLabel.Label; hasDefaultLabel = hasDefaultLabel || boundSwitchLabel.ConstantValueOpt == null; SetState(breakState.Clone()); var simulatedGoto = new BoundGotoStatement(node.Syntax, label); VisitGotoStatement(simulatedGoto); } } if (hasDefaultLabel) { // Condition (1) for an unreachable break state is satisfied breakState = UnreachableState(); } } else if (!node.BreakLabel.Equals(constantTargetOpt)) { SetState(breakState.Clone()); var simulatedGoto = new BoundGotoStatement(node.Syntax, constantTargetOpt); VisitGotoStatement(simulatedGoto); // Condition (1) or (2) for an unreachable break state is satisfied breakState = UnreachableState(); } return(breakState); }
public override BoundNode VisitGotoStatement(BoundGotoStatement node) { // we are removing the label expressions from the bound tree because this expression is no longer needed // for the emit phase. It is even doing harm to e.g. the stack depth calculation because because this expression // would not need to be pushed to the stack. BoundExpression caseExpressionOpt = null; // we are removing the label expressions from the bound tree because this expression is no longer needed // for the emit phase. It is even doing harm to e.g. the stack depth calculation because because this expression // would not need to be pushed to the stack. BoundLabel labelExpressionOpt = null; return(AddSequencePoint(node.Update(node.Label, caseExpressionOpt, labelExpressionOpt))); }
public override BoundNode VisitGotoStatement(BoundGotoStatement node) { // we are removing the label expressions from the bound tree because this expression is no longer needed // for the emit phase. It is even doing harm to e.g. the stack depth calculation because because this expression // would not need to be pushed to the stack. BoundExpression caseExpressionOpt = null; // we are removing the label expressions from the bound tree because this expression is no longer needed // for the emit phase. It is even doing harm to e.g. the stack depth calculation because because this expression // would not need to be pushed to the stack. BoundLabel labelExpressionOpt = null; return AddSequencePoint(node.Update(node.Label, caseExpressionOpt, labelExpressionOpt)); }
private BoundStatement RewriteWhileStatement( CSharpSyntaxNode syntax, BoundExpression rewrittenCondition, TextSpan conditionSequencePointSpan, BoundStatement rewrittenBody, GeneratedLabelSymbol breakLabel, GeneratedLabelSymbol continueLabel, bool hasErrors) { var startLabel = new GeneratedLabelSymbol("start"); BoundStatement ifConditionGotoStart = new BoundConditionalGoto(rewrittenCondition.Syntax, rewrittenCondition, true, startLabel); if (this.GenerateDebugInfo) { ifConditionGotoStart = new BoundSequencePointWithSpan(syntax, ifConditionGotoStart, conditionSequencePointSpan); } // while (condition) // body; // // becomes // // goto continue; // start: // { // body // continue: // GotoIfTrue condition start; // } // break: BoundStatement gotoContinue = new BoundGotoStatement(syntax, continueLabel); if (this.GenerateDebugInfo) { // mark the initial jump as hidden. We do it to tell that this is not a part of previous statement. This // jump may be a target of another jump (for example if loops are nested) and that would give the // impression that the previous statement is being re-executed. gotoContinue = new BoundSequencePoint(null, gotoContinue); } return(BoundStatementList.Synthesized(syntax, hasErrors, gotoContinue, new BoundLabelStatement(syntax, startLabel), rewrittenBody, new BoundLabelStatement(syntax, continueLabel), ifConditionGotoStart, new BoundLabelStatement(syntax, breakLabel))); }
private BoundStatement RewriteWhileStatement( CSharpSyntaxNode syntax, BoundExpression rewrittenCondition, TextSpan conditionSequencePointSpan, BoundStatement rewrittenBody, GeneratedLabelSymbol breakLabel, GeneratedLabelSymbol continueLabel, bool hasErrors) { var startLabel = new GeneratedLabelSymbol("start"); BoundStatement ifConditionGotoStart = new BoundConditionalGoto(rewrittenCondition.Syntax, rewrittenCondition, true, startLabel); if (this.GenerateDebugInfo) { ifConditionGotoStart = new BoundSequencePointWithSpan(syntax, ifConditionGotoStart, conditionSequencePointSpan); } // while (condition) // body; // // becomes // // goto continue; // start: // { // body // continue: // GotoIfTrue condition start; // } // break: BoundStatement gotoContinue = new BoundGotoStatement(syntax, continueLabel); if (this.GenerateDebugInfo) { // mark the initial jump as hidden. We do it to tell that this is not a part of previous statement. This // jump may be a target of another jump (for example if loops are nested) and that would give the // impression that the previous statement is being re-executed. gotoContinue = new BoundSequencePoint(null, gotoContinue); } return BoundStatementList.Synthesized(syntax, hasErrors, gotoContinue, new BoundLabelStatement(syntax, startLabel), rewrittenBody, new BoundLabelStatement(syntax, continueLabel), ifConditionGotoStart, new BoundLabelStatement(syntax, breakLabel)); }
public override BoundNode VisitGotoStatement(BoundGotoStatement node) { // we are removing the label expressions from the bound tree because this expression is no longer needed // for the emit phase. It is even doing harm to e.g. the stack depth calculation because this expression // would not need to be pushed to the stack. BoundExpression caseExpressionOpt = null; // we are removing the label expressions from the bound tree because this expression is no longer needed // for the emit phase. It is even doing harm to e.g. the stack depth calculation because this expression // would not need to be pushed to the stack. BoundLabel labelExpressionOpt = null; BoundStatement result = node.Update(node.Label, caseExpressionOpt, labelExpressionOpt); if (this.Instrument && !node.WasCompilerGenerated) { result = _instrumenter.InstrumentGotoStatement(node, result); } return result; }
public override BoundNode VisitGotoStatement(BoundGotoStatement node) { // we are removing the label expressions from the bound tree because this expression is no longer needed // for the emit phase. It is even doing harm to e.g. the stack depth calculation because this expression // would not need to be pushed to the stack. BoundExpression caseExpressionOpt = null; // we are removing the label expressions from the bound tree because this expression is no longer needed // for the emit phase. It is even doing harm to e.g. the stack depth calculation because this expression // would not need to be pushed to the stack. BoundLabel labelExpressionOpt = null; BoundStatement result = node.Update(node.Label, caseExpressionOpt, labelExpressionOpt); if (this.Instrument && !node.WasCompilerGenerated) { result = _instrumenter.InstrumentGotoStatement(node, result); } return(result); }
public override BoundStatement InstrumentGotoStatement(BoundGotoStatement original, BoundStatement rewritten) { return Previous.InstrumentGotoStatement(original, rewritten); }
private BoundStatement RewriteWhileStatement( CSharpSyntaxNode syntax, ImmutableArray <LocalSymbol> innerLocals, BoundExpression rewrittenCondition, TextSpan conditionSequencePointSpan, BoundStatement rewrittenBody, GeneratedLabelSymbol breakLabel, GeneratedLabelSymbol continueLabel, bool hasErrors) { if (!innerLocals.IsDefaultOrEmpty) { var walker = new AnyLocalCapturedInALambdaWalker(innerLocals); if (walker.Analyze(rewrittenCondition) || walker.Analyze(rewrittenBody)) { // If any inner local is captured within a lambda, we need to enter scope-block // always from the top, that is where an instance of a display class will be created. // The IL will be less optimal, but this shouldn't be a problem, given presence of lambdas. // while (condition) // body; // // becomes // // continue: // { // GotoIfFalse condition break; // body // goto continue; // } // break: // TODO: We could perform more fine analysis. // If locals declared in condition (the innerLocals) are captured, but not referenced in the body, we could use optimal IL by creating // another block around the condition and use it as a scope for the locals declared in condition. // This optimization can be applied to 'for' as well, while-body === for-body + increment. // Note however that the scope adjusments will likely be observable during debugging, in locals window. BoundStatement ifNotConditionGotoBreak = new BoundConditionalGoto(rewrittenCondition.Syntax, rewrittenCondition, false, breakLabel); if (this.generateDebugInfo) { ifNotConditionGotoBreak = new BoundSequencePointWithSpan(syntax, ifNotConditionGotoBreak, conditionSequencePointSpan); } return(BoundStatementList.Synthesized(syntax, hasErrors, new BoundLabelStatement(syntax, continueLabel), new BoundBlock(syntax, innerLocals, ImmutableArray.Create <BoundStatement>( ifNotConditionGotoBreak, rewrittenBody, new BoundGotoStatement(syntax, continueLabel))), new BoundLabelStatement(syntax, breakLabel))); } } var startLabel = new GeneratedLabelSymbol("start"); BoundStatement ifConditionGotoStart = new BoundConditionalGoto(rewrittenCondition.Syntax, rewrittenCondition, true, startLabel); if (this.generateDebugInfo) { ifConditionGotoStart = new BoundSequencePointWithSpan(syntax, ifConditionGotoStart, conditionSequencePointSpan); } // while (condition) // body; // // becomes // // goto continue; // start: // { // body // continue: // GotoIfTrue condition start; // } // break: BoundStatement gotoContinue = new BoundGotoStatement(syntax, continueLabel); if (this.generateDebugInfo) { // mark the initial jump as hidden. We do it to tell that this is not a part of previous statement. This // jump may be a target of another jump (for example if loops are nested) and that would give the // impression that the previous statement is being re-executed. gotoContinue = new BoundSequencePoint(null, gotoContinue); } if (!innerLocals.IsDefaultOrEmpty) { return(BoundStatementList.Synthesized(syntax, hasErrors, gotoContinue, new BoundLabelStatement(syntax, startLabel), new BoundBlock(syntax, innerLocals, ImmutableArray.Create <BoundStatement>( rewrittenBody, new BoundLabelStatement(syntax, continueLabel), ifConditionGotoStart)), new BoundLabelStatement(syntax, breakLabel))); } return(BoundStatementList.Synthesized(syntax, hasErrors, gotoContinue, new BoundLabelStatement(syntax, startLabel), rewrittenBody, new BoundLabelStatement(syntax, continueLabel), ifConditionGotoStart, new BoundLabelStatement(syntax, breakLabel))); }
private BoundStatement RewriteForStatement( CSharpSyntaxNode syntax, ImmutableArray <LocalSymbol> outerLocals, BoundStatement rewrittenInitializer, ImmutableArray <LocalSymbol> innerLocals, BoundExpression rewrittenCondition, SyntaxNodeOrToken conditionSyntax, BoundStatement rewrittenIncrement, BoundStatement rewrittenBody, GeneratedLabelSymbol breakLabel, GeneratedLabelSymbol continueLabel, bool hasErrors) { Debug.Assert(rewrittenBody != null); // The sequence point behavior exhibited here is different from that of the native compiler. In the native // compiler, if you have something like // // for(int i = 0, j = 0; ; i++, j++) // ^--------------^ ^------^ // // then all the initializers are treated as a single sequence point, as are // all the loop incrementers. // // We now make each one individually a sequence point: // // for(int i = 0, j = 0; ; i++, j++) // ^-------^ ^---^ ^-^ ^-^ // // If we decide that we want to preserve the native compiler stepping behavior // then we'll need to be a bit fancy here. The initializer and increment statements // can contain lambdas whose bodies need to have sequence points inserted, so we // need to make sure we visit the children. But we'll also need to make sure that // we do not generate one sequence point for each statement in the initializers // and the incrementers. var statementBuilder = ArrayBuilder <BoundStatement> .GetInstance(); if (rewrittenInitializer != null) { statementBuilder.Add(rewrittenInitializer); } var startLabel = new GeneratedLabelSymbol("start"); if (!innerLocals.IsDefaultOrEmpty) { var walker = new AnyLocalCapturedInALambdaWalker(innerLocals); if (walker.Analyze(rewrittenCondition) || walker.Analyze(rewrittenIncrement) || walker.Analyze(rewrittenBody)) { // If any inner local is captured within a lambda, we need to enter scope-block // always from the top, that is where an instance of a display class will be created. // The IL will be less optimal, but this shouldn't be a problem, given presence of lambdas. // for (initializer; condition; increment) // body; // // becomes the following (with // block added for locals) // // { // initializer; // start: // { // GotoIfFalse condition break; // body; // continue: // increment; // goto start; // } // break: // } // start: statementBuilder.Add(new BoundLabelStatement(syntax, startLabel)); var blockBuilder = ArrayBuilder <BoundStatement> .GetInstance(); // GotoIfFalse condition break; if (rewrittenCondition != null) { BoundStatement ifNotConditionGotoBreak = new BoundConditionalGoto(rewrittenCondition.Syntax, rewrittenCondition, false, breakLabel); if (this.generateDebugInfo) { if (conditionSyntax.IsToken) { ifNotConditionGotoBreak = new BoundSequencePointWithSpan(syntax, ifNotConditionGotoBreak, conditionSyntax.Span); } else { ifNotConditionGotoBreak = new BoundSequencePoint((CSharpSyntaxNode)conditionSyntax.AsNode(), ifNotConditionGotoBreak); } } blockBuilder.Add(ifNotConditionGotoBreak); } // body; blockBuilder.Add(rewrittenBody); // continue: // increment; blockBuilder.Add(new BoundLabelStatement(syntax, continueLabel)); if (rewrittenIncrement != null) { blockBuilder.Add(rewrittenIncrement); } // goto start; blockBuilder.Add(new BoundGotoStatement(syntax, startLabel)); statementBuilder.Add(new BoundBlock(syntax, innerLocals, blockBuilder.ToImmutableAndFree())); // break: statementBuilder.Add(new BoundLabelStatement(syntax, breakLabel)); return(new BoundBlock(syntax, outerLocals, statementBuilder.ToImmutableAndFree(), hasErrors)); } } var endLabel = new GeneratedLabelSymbol("end"); // for (initializer; condition; increment) // body; // // becomes the following (with // block added for locals) // // { // initializer; // goto end; // start: // body; // continue: // increment; // end: // GotoIfTrue condition start; // break: // } // initializer; // goto end; //mark the initial jump as hidden. //We do it to tell that this is not a part of previous statement. //This jump may be a target of another jump (for example if loops are nested) and that will make //impression of the previous statement being re-executed var gotoEnd = new BoundSequencePoint(null, new BoundGotoStatement(syntax, endLabel)); statementBuilder.Add(gotoEnd); // start: // body; statementBuilder.Add(new BoundLabelStatement(syntax, startLabel)); ArrayBuilder <BoundStatement> saveBuilder = null; if (!innerLocals.IsDefaultOrEmpty) { saveBuilder = statementBuilder; statementBuilder = ArrayBuilder <BoundStatement> .GetInstance(); } statementBuilder.Add(rewrittenBody); // continue: // increment; statementBuilder.Add(new BoundLabelStatement(syntax, continueLabel)); if (rewrittenIncrement != null) { statementBuilder.Add(rewrittenIncrement); } // end: // GotoIfTrue condition start; statementBuilder.Add(new BoundLabelStatement(syntax, endLabel)); BoundStatement branchBack = null; if (rewrittenCondition != null) { branchBack = new BoundConditionalGoto(rewrittenCondition.Syntax, rewrittenCondition, true, startLabel); } else { branchBack = new BoundGotoStatement(syntax, startLabel); } if (this.generateDebugInfo) { if (conditionSyntax.IsToken) { branchBack = new BoundSequencePointWithSpan(syntax, branchBack, conditionSyntax.Span); } else { //if there is no condition, make this a hidden point so that //it does not count as a part of previous statement branchBack = new BoundSequencePoint((CSharpSyntaxNode)conditionSyntax.AsNode(), branchBack); } } statementBuilder.Add(branchBack); if (!innerLocals.IsDefaultOrEmpty) { var block = new BoundBlock(syntax, innerLocals, statementBuilder.ToImmutableAndFree()); statementBuilder = saveBuilder; statementBuilder.Add(block); } // break: statementBuilder.Add(new BoundLabelStatement(syntax, breakLabel)); var statements = statementBuilder.ToImmutableAndFree(); return(new BoundBlock(syntax, outerLocals, statements, hasErrors)); }
public override BoundNode VisitBreakStatement(BoundBreakStatement node) { var result = new BoundGotoStatement(node.Syntax, node.Label, node.HasErrors); return AddSequencePoint(result); }
public override BoundNode VisitGotoStatement(BoundGotoStatement node) { AddGoto(node.Label); return base.VisitGotoStatement(node); }
private BoundStatement RewriteWhileStatement( BoundLoopStatement loop, BoundExpression rewrittenCondition, BoundStatement rewrittenBody, GeneratedLabelSymbol breakLabel, GeneratedLabelSymbol continueLabel, bool hasErrors) { Debug.Assert(loop.Kind == BoundKind.WhileStatement || loop.Kind == BoundKind.ForEachStatement); SyntaxNode syntax = loop.Syntax; var startLabel = new GeneratedLabelSymbol("start"); BoundStatement ifConditionGotoStart = new BoundConditionalGoto(rewrittenCondition.Syntax, rewrittenCondition, true, startLabel); if (this.Instrument && !loop.WasCompilerGenerated) { switch (loop.Kind) { case BoundKind.WhileStatement: ifConditionGotoStart = _instrumenter.InstrumentWhileStatementConditionalGotoStart((BoundWhileStatement)loop, ifConditionGotoStart); break; case BoundKind.ForEachStatement: ifConditionGotoStart = _instrumenter.InstrumentForEachStatementConditionalGotoStart((BoundForEachStatement)loop, ifConditionGotoStart); break; default: throw ExceptionUtilities.UnexpectedValue(loop.Kind); } } // while (condition) // body; // // becomes // // goto continue; // start: // { // body // continue: // GotoIfTrue condition start; // } // break: BoundStatement gotoContinue = new BoundGotoStatement(syntax, continueLabel); if (this.Instrument && !loop.WasCompilerGenerated) { // mark the initial jump as hidden. We do it to tell that this is not a part of previous statement. This // jump may be a target of another jump (for example if loops are nested) and that would give the // impression that the previous statement is being re-executed. switch (loop.Kind) { case BoundKind.WhileStatement: gotoContinue = _instrumenter.InstrumentWhileStatementGotoContinue((BoundWhileStatement)loop, gotoContinue); break; case BoundKind.ForEachStatement: gotoContinue = _instrumenter.InstrumentForEachStatementGotoContinue((BoundForEachStatement)loop, gotoContinue); break; default: throw ExceptionUtilities.UnexpectedValue(loop.Kind); } } return(BoundStatementList.Synthesized(syntax, hasErrors, gotoContinue, new BoundLabelStatement(syntax, startLabel), rewrittenBody, new BoundLabelStatement(syntax, continueLabel), ifConditionGotoStart, new BoundLabelStatement(syntax, breakLabel))); }
public override BoundStatement InstrumentGotoStatement(BoundGotoStatement original, BoundStatement rewritten) { return AddDynamicAnalysis(original, base.InstrumentGotoStatement(original, rewritten)); }
private BoundStatement RewriteForStatement( BoundLoopStatement original, ImmutableArray<LocalSymbol> outerLocals, BoundStatement rewrittenInitializer, BoundExpression rewrittenCondition, BoundStatement rewrittenIncrement, BoundStatement rewrittenBody, GeneratedLabelSymbol breakLabel, GeneratedLabelSymbol continueLabel, bool hasErrors) { Debug.Assert(original.Kind == BoundKind.ForStatement || original.Kind == BoundKind.ForEachStatement); Debug.Assert(rewrittenBody != null); // The sequence point behavior exhibited here is different from that of the native compiler. In the native // compiler, if you have something like // // for([|int i = 0, j = 0|]; ; [|i++, j++|]) // // then all the initializers are treated as a single sequence point, as are // all the loop incrementors. // // We now make each one individually a sequence point: // // for([|int i = 0|], [|j = 0|]; ; [|i++|], [|j++|]) // // If we decide that we want to preserve the native compiler stepping behavior // then we'll need to be a bit fancy here. The initializer and increment statements // can contain lambdas whose bodies need to have sequence points inserted, so we // need to make sure we visit the children. But we'll also need to make sure that // we do not generate one sequence point for each statement in the initializers // and the incrementors. SyntaxNode syntax = original.Syntax; var statementBuilder = ArrayBuilder<BoundStatement>.GetInstance(); if (rewrittenInitializer != null) { statementBuilder.Add(rewrittenInitializer); } var startLabel = new GeneratedLabelSymbol("start"); // for (initializer; condition; increment) // body; // // becomes the following (with block added for locals) // // { // initializer; // goto end; // start: // body; // continue: // increment; // end: // GotoIfTrue condition start; // break: // } var endLabel = new GeneratedLabelSymbol("end"); // initializer; // goto end; BoundStatement gotoEnd = new BoundGotoStatement(syntax, endLabel); if (this.Instrument) { switch (original.Kind) { case BoundKind.ForEachStatement: gotoEnd = _instrumenter.InstrumentForEachStatementGotoEnd((BoundForEachStatement)original, gotoEnd); break; case BoundKind.ForStatement: gotoEnd = _instrumenter.InstrumentForStatementGotoEnd((BoundForStatement)original, gotoEnd); break; default: throw ExceptionUtilities.UnexpectedValue(original.Kind); } } statementBuilder.Add(gotoEnd); // start: // body; statementBuilder.Add(new BoundLabelStatement(syntax, startLabel)); statementBuilder.Add(rewrittenBody); // continue: // increment; statementBuilder.Add(new BoundLabelStatement(syntax, continueLabel)); if (rewrittenIncrement != null) { statementBuilder.Add(rewrittenIncrement); } // end: // GotoIfTrue condition start; statementBuilder.Add(new BoundLabelStatement(syntax, endLabel)); BoundStatement branchBack = null; if (rewrittenCondition != null) { branchBack = new BoundConditionalGoto(rewrittenCondition.Syntax, rewrittenCondition, true, startLabel); } else { branchBack = new BoundGotoStatement(syntax, startLabel); } if (this.Instrument) { switch (original.Kind) { case BoundKind.ForEachStatement: branchBack = _instrumenter.InstrumentForEachStatementConditionalGotoStart((BoundForEachStatement)original, branchBack); break; case BoundKind.ForStatement: branchBack = _instrumenter.InstrumentForStatementConditionalGotoStart((BoundForStatement)original, branchBack); break; default: throw ExceptionUtilities.UnexpectedValue(original.Kind); } } statementBuilder.Add(branchBack); // break: statementBuilder.Add(new BoundLabelStatement(syntax, breakLabel)); var statements = statementBuilder.ToImmutableAndFree(); return new BoundBlock(syntax, outerLocals, ImmutableArray<LocalFunctionSymbol>.Empty, statements, hasErrors); }
public override BoundNode VisitGotoStatement(BoundGotoStatement node) { _labelsUsed.Add(node.Label); return(base.VisitGotoStatement(node)); }
public override BoundStatement InstrumentGotoStatement(BoundGotoStatement original, BoundStatement rewritten) { return AddSequencePoint(base.InstrumentGotoStatement(original, rewritten)); }
public override BoundNode VisitGotoStatement(BoundGotoStatement node) { BoundExpression caseExpressionOpt = (BoundExpression)this.Visit(node.CaseExpressionOpt); BoundLabel labelExpressionOpt = (BoundLabel)this.Visit(node.LabelExpressionOpt); var proxyLabel = _currentAwaitFinallyFrame.ProxyLabelIfNeeded(node.Label); return node.Update(proxyLabel, caseExpressionOpt, labelExpressionOpt); }
private BoundStatement RewriteForStatement( CSharpSyntaxNode syntax, ImmutableArray<LocalSymbol> outerLocals, BoundStatement rewrittenInitializer, ImmutableArray<LocalSymbol> innerLocals, BoundExpression rewrittenCondition, SyntaxNodeOrToken conditionSyntax, BoundStatement rewrittenIncrement, BoundStatement rewrittenBody, GeneratedLabelSymbol breakLabel, GeneratedLabelSymbol continueLabel, bool hasErrors) { Debug.Assert(rewrittenBody != null); // The sequence point behavior exhibited here is different from that of the native compiler. In the native // compiler, if you have something like // // for(int i = 0, j = 0; ; i++, j++) // ^--------------^ ^------^ // // then all the initializers are treated as a single sequence point, as are // all the loop incrementers. // // We now make each one individually a sequence point: // // for(int i = 0, j = 0; ; i++, j++) // ^-------^ ^---^ ^-^ ^-^ // // If we decide that we want to preserve the native compiler stepping behavior // then we'll need to be a bit fancy here. The initializer and increment statements // can contain lambdas whose bodies need to have sequence points inserted, so we // need to make sure we visit the children. But we'll also need to make sure that // we do not generate one sequence point for each statement in the initializers // and the incrementers. var statementBuilder = ArrayBuilder<BoundStatement>.GetInstance(); if (rewrittenInitializer != null) { statementBuilder.Add(rewrittenInitializer); } var startLabel = new GeneratedLabelSymbol("start"); if (!innerLocals.IsDefaultOrEmpty) { var walker = new AnyLocalCapturedInALambdaWalker(innerLocals); if (walker.Analyze(rewrittenCondition) || walker.Analyze(rewrittenIncrement) || walker.Analyze(rewrittenBody)) { // If any inner local is captured within a lambda, we need to enter scope-block // always from the top, that is where an instance of a display class will be created. // The IL will be less optimal, but this shouldn't be a problem, given presence of lambdas. // for (initializer; condition; increment) // body; // // becomes the following (with // block added for locals) // // { // initializer; // start: // { // GotoIfFalse condition break; // body; // continue: // increment; // goto start; // } // break: // } // start: statementBuilder.Add(new BoundLabelStatement(syntax, startLabel)); var blockBuilder = ArrayBuilder<BoundStatement>.GetInstance(); // GotoIfFalse condition break; if (rewrittenCondition != null) { BoundStatement ifNotConditionGotoBreak = new BoundConditionalGoto(rewrittenCondition.Syntax, rewrittenCondition, false, breakLabel); if (this.generateDebugInfo) { if (conditionSyntax.IsToken) { ifNotConditionGotoBreak = new BoundSequencePointWithSpan(syntax, ifNotConditionGotoBreak, conditionSyntax.Span); } else { ifNotConditionGotoBreak = new BoundSequencePoint((CSharpSyntaxNode)conditionSyntax.AsNode(), ifNotConditionGotoBreak); } } blockBuilder.Add(ifNotConditionGotoBreak); } // body; blockBuilder.Add(rewrittenBody); // continue: // increment; blockBuilder.Add(new BoundLabelStatement(syntax, continueLabel)); if (rewrittenIncrement != null) { blockBuilder.Add(rewrittenIncrement); } // goto start; blockBuilder.Add(new BoundGotoStatement(syntax, startLabel)); statementBuilder.Add(new BoundBlock(syntax, innerLocals, blockBuilder.ToImmutableAndFree())); // break: statementBuilder.Add(new BoundLabelStatement(syntax, breakLabel)); return new BoundBlock(syntax, outerLocals, statementBuilder.ToImmutableAndFree(), hasErrors); } } var endLabel = new GeneratedLabelSymbol("end"); // for (initializer; condition; increment) // body; // // becomes the following (with // block added for locals) // // { // initializer; // goto end; // start: // body; // continue: // increment; // end: // GotoIfTrue condition start; // break: // } // initializer; // goto end; //mark the initial jump as hidden. //We do it to tell that this is not a part of previous statement. //This jump may be a target of another jump (for example if loops are nested) and that will make //impression of the previous statement being re-executed var gotoEnd = new BoundSequencePoint(null, new BoundGotoStatement(syntax, endLabel)); statementBuilder.Add(gotoEnd); // start: // body; statementBuilder.Add(new BoundLabelStatement(syntax, startLabel)); ArrayBuilder<BoundStatement> saveBuilder = null; if (!innerLocals.IsDefaultOrEmpty) { saveBuilder = statementBuilder; statementBuilder = ArrayBuilder<BoundStatement>.GetInstance(); } statementBuilder.Add(rewrittenBody); // continue: // increment; statementBuilder.Add(new BoundLabelStatement(syntax, continueLabel)); if (rewrittenIncrement != null) { statementBuilder.Add(rewrittenIncrement); } // end: // GotoIfTrue condition start; statementBuilder.Add(new BoundLabelStatement(syntax, endLabel)); BoundStatement branchBack = null; if (rewrittenCondition != null) { branchBack = new BoundConditionalGoto(rewrittenCondition.Syntax, rewrittenCondition, true, startLabel); } else { branchBack = new BoundGotoStatement(syntax, startLabel); } if (this.generateDebugInfo) { if (conditionSyntax.IsToken) { branchBack = new BoundSequencePointWithSpan(syntax, branchBack, conditionSyntax.Span); } else { //if there is no condition, make this a hidden point so that //it does not count as a part of previous statement branchBack = new BoundSequencePoint((CSharpSyntaxNode)conditionSyntax.AsNode(), branchBack); } } statementBuilder.Add(branchBack); if (!innerLocals.IsDefaultOrEmpty) { var block = new BoundBlock(syntax, innerLocals, statementBuilder.ToImmutableAndFree()); statementBuilder = saveBuilder; statementBuilder.Add(block); } // break: statementBuilder.Add(new BoundLabelStatement(syntax, breakLabel)); var statements = statementBuilder.ToImmutableAndFree(); return new BoundBlock(syntax, outerLocals, statements, hasErrors); }
private BoundStatement RewriteForStatement( CSharpSyntaxNode syntax, ImmutableArray<LocalSymbol> outerLocals, BoundStatement rewrittenInitializer, BoundExpression rewrittenCondition, CSharpSyntaxNode conditionSyntaxOpt, TextSpan conditionSpanOpt, BoundStatement rewrittenIncrement, BoundStatement rewrittenBody, GeneratedLabelSymbol breakLabel, GeneratedLabelSymbol continueLabel, bool hasErrors) { Debug.Assert(rewrittenBody != null); // The sequence point behavior exhibited here is different from that of the native compiler. In the native // compiler, if you have something like // // for([|int i = 0, j = 0|]; ; [|i++, j++|]) // // then all the initializers are treated as a single sequence point, as are // all the loop incrementors. // // We now make each one individually a sequence point: // // for([|int i = 0|], [|j = 0|]; ; [|i++|], [|j++|]) // // If we decide that we want to preserve the native compiler stepping behavior // then we'll need to be a bit fancy here. The initializer and increment statements // can contain lambdas whose bodies need to have sequence points inserted, so we // need to make sure we visit the children. But we'll also need to make sure that // we do not generate one sequence point for each statement in the initializers // and the incrementors. var statementBuilder = ArrayBuilder<BoundStatement>.GetInstance(); if (rewrittenInitializer != null) { statementBuilder.Add(rewrittenInitializer); } var startLabel = new GeneratedLabelSymbol("start"); // for (initializer; condition; increment) // body; // // becomes the following (with block added for locals) // // { // initializer; // goto end; // start: // body; // continue: // increment; // end: // GotoIfTrue condition start; // break: // } var endLabel = new GeneratedLabelSymbol("end"); // initializer; // goto end; // Mark the initial jump as hidden. // We do it to tell that this is not a part of previous statement. // This jump may be a target of another jump (for example if loops are nested) and that will make // impression of the previous statement being re-executed var gotoEnd = new BoundSequencePoint(null, new BoundGotoStatement(syntax, endLabel)); statementBuilder.Add(gotoEnd); // start: // body; statementBuilder.Add(new BoundLabelStatement(syntax, startLabel)); statementBuilder.Add(rewrittenBody); // continue: // increment; statementBuilder.Add(new BoundLabelStatement(syntax, continueLabel)); if (rewrittenIncrement != null) { statementBuilder.Add(rewrittenIncrement); } // end: // GotoIfTrue condition start; statementBuilder.Add(new BoundLabelStatement(syntax, endLabel)); BoundStatement branchBack = null; if (rewrittenCondition != null) { branchBack = new BoundConditionalGoto(rewrittenCondition.Syntax, rewrittenCondition, true, startLabel); } else { branchBack = new BoundGotoStatement(syntax, startLabel); } if (this.GenerateDebugInfo) { if (!conditionSpanOpt.IsEmpty) { branchBack = new BoundSequencePointWithSpan(syntax, branchBack, conditionSpanOpt); } else { // hidden sequence point if there is no condition branchBack = new BoundSequencePoint(conditionSyntaxOpt, branchBack); } } statementBuilder.Add(branchBack); // break: statementBuilder.Add(new BoundLabelStatement(syntax, breakLabel)); var statements = statementBuilder.ToImmutableAndFree(); return new BoundBlock(syntax, outerLocals, statements, hasErrors); }
private BoundStatement RewriteWhileStatement( CSharpSyntaxNode syntax, ImmutableArray<LocalSymbol> innerLocals, BoundExpression rewrittenCondition, TextSpan conditionSequencePointSpan, BoundStatement rewrittenBody, GeneratedLabelSymbol breakLabel, GeneratedLabelSymbol continueLabel, bool hasErrors) { if (!innerLocals.IsDefaultOrEmpty) { var walker = new AnyLocalCapturedInALambdaWalker(innerLocals); if (walker.Analyze(rewrittenCondition) || walker.Analyze(rewrittenBody)) { // If any inner local is captured within a lambda, we need to enter scope-block // always from the top, that is where an instance of a display class will be created. // The IL will be less optimal, but this shouldn't be a problem, given presence of lambdas. // while (condition) // body; // // becomes // // continue: // { // GotoIfFalse condition break; // body // goto continue; // } // break: // TODO: We could perform more fine analysis. // If locals declared in condition (the innerLocals) are captured, but not referenced in the body, we could use optimal IL by creating // another block around the condition and use it as a scope for the locals declared in condition. // This optimization can be applied to 'for' as well, while-body === for-body + increment. // Note however that the scope adjusments will likely be observable during debugging, in locals window. BoundStatement ifNotConditionGotoBreak = new BoundConditionalGoto(rewrittenCondition.Syntax, rewrittenCondition, false, breakLabel); if (this.GenerateDebugInfo) { ifNotConditionGotoBreak = new BoundSequencePointWithSpan(syntax, ifNotConditionGotoBreak, conditionSequencePointSpan); } return BoundStatementList.Synthesized(syntax, hasErrors, new BoundLabelStatement(syntax, continueLabel), new BoundBlock(syntax, innerLocals, ImmutableArray.Create( ifNotConditionGotoBreak, rewrittenBody, new BoundGotoStatement(syntax, continueLabel))), new BoundLabelStatement(syntax, breakLabel)); } } var startLabel = new GeneratedLabelSymbol("start"); BoundStatement ifConditionGotoStart = new BoundConditionalGoto(rewrittenCondition.Syntax, rewrittenCondition, true, startLabel); if (this.GenerateDebugInfo) { ifConditionGotoStart = new BoundSequencePointWithSpan(syntax, ifConditionGotoStart, conditionSequencePointSpan); } // while (condition) // body; // // becomes // // goto continue; // start: // { // body // continue: // GotoIfTrue condition start; // } // break: BoundStatement gotoContinue = new BoundGotoStatement(syntax, continueLabel); if (this.GenerateDebugInfo) { // mark the initial jump as hidden. We do it to tell that this is not a part of previous statement. This // jump may be a target of another jump (for example if loops are nested) and that would give the // impression that the previous statement is being re-executed. gotoContinue = new BoundSequencePoint(null, gotoContinue); } if (!innerLocals.IsDefaultOrEmpty) { return BoundStatementList.Synthesized(syntax, hasErrors, gotoContinue, new BoundLabelStatement(syntax, startLabel), new BoundBlock(syntax, innerLocals, ImmutableArray.Create<BoundStatement>( rewrittenBody, new BoundLabelStatement(syntax, continueLabel), ifConditionGotoStart)), new BoundLabelStatement(syntax, breakLabel)); } return BoundStatementList.Synthesized(syntax, hasErrors, gotoContinue, new BoundLabelStatement(syntax, startLabel), rewrittenBody, new BoundLabelStatement(syntax, continueLabel), ifConditionGotoStart, new BoundLabelStatement(syntax, breakLabel)); }
public override BoundNode VisitGotoStatement(BoundGotoStatement node) { BoundExpression caseExpressionOpt = (BoundExpression)this.Visit(node.CaseExpressionOpt); BoundLabel labelExpressionOpt = (BoundLabel)this.Visit(node.LabelExpressionOpt); var proxyLabel = _currentFinallyFrame.ProxyLabelIfNeeded(node.Label); Debug.Assert(node.Label == proxyLabel || !(F.CurrentMethod is IteratorFinallyMethodSymbol), "should not be proxying branches in finally"); return node.Update(proxyLabel, caseExpressionOpt, labelExpressionOpt); }
private BoundStatement RewriteForStatement( CSharpSyntaxNode syntax, ImmutableArray <LocalSymbol> outerLocals, BoundStatement rewrittenInitializer, BoundExpression rewrittenCondition, CSharpSyntaxNode conditionSyntaxOpt, TextSpan conditionSpanOpt, BoundStatement rewrittenIncrement, BoundStatement rewrittenBody, GeneratedLabelSymbol breakLabel, GeneratedLabelSymbol continueLabel, bool hasErrors) { Debug.Assert(rewrittenBody != null); // The sequence point behavior exhibited here is different from that of the native compiler. In the native // compiler, if you have something like // // for([|int i = 0, j = 0|]; ; [|i++, j++|]) // // then all the initializers are treated as a single sequence point, as are // all the loop incrementers. // // We now make each one individually a sequence point: // // for([|int i = 0|], [|j = 0|]; ; [|i++|], [|j++|]) // // If we decide that we want to preserve the native compiler stepping behavior // then we'll need to be a bit fancy here. The initializer and increment statements // can contain lambdas whose bodies need to have sequence points inserted, so we // need to make sure we visit the children. But we'll also need to make sure that // we do not generate one sequence point for each statement in the initializers // and the incrementers. var statementBuilder = ArrayBuilder <BoundStatement> .GetInstance(); if (rewrittenInitializer != null) { statementBuilder.Add(rewrittenInitializer); } var startLabel = new GeneratedLabelSymbol("start"); // for (initializer; condition; increment) // body; // // becomes the following (with block added for locals) // // { // initializer; // goto end; // start: // body; // continue: // increment; // end: // GotoIfTrue condition start; // break: // } var endLabel = new GeneratedLabelSymbol("end"); // initializer; // goto end; // Mark the initial jump as hidden. // We do it to tell that this is not a part of previous statement. // This jump may be a target of another jump (for example if loops are nested) and that will make // impression of the previous statement being re-executed var gotoEnd = new BoundSequencePoint(null, new BoundGotoStatement(syntax, endLabel)); statementBuilder.Add(gotoEnd); // start: // body; statementBuilder.Add(new BoundLabelStatement(syntax, startLabel)); statementBuilder.Add(rewrittenBody); // continue: // increment; statementBuilder.Add(new BoundLabelStatement(syntax, continueLabel)); if (rewrittenIncrement != null) { statementBuilder.Add(rewrittenIncrement); } // end: // GotoIfTrue condition start; statementBuilder.Add(new BoundLabelStatement(syntax, endLabel)); BoundStatement branchBack = null; if (rewrittenCondition != null) { branchBack = new BoundConditionalGoto(rewrittenCondition.Syntax, rewrittenCondition, true, startLabel); } else { branchBack = new BoundGotoStatement(syntax, startLabel); } if (this.GenerateDebugInfo) { if (!conditionSpanOpt.IsEmpty) { branchBack = new BoundSequencePointWithSpan(syntax, branchBack, conditionSpanOpt); } else { // hidden sequence point if there is no condition branchBack = new BoundSequencePoint(conditionSyntaxOpt, branchBack); } } statementBuilder.Add(branchBack); // break: statementBuilder.Add(new BoundLabelStatement(syntax, breakLabel)); var statements = statementBuilder.ToImmutableAndFree(); return(new BoundBlock(syntax, outerLocals, statements, hasErrors)); }
public virtual BoundStatement InstrumentGotoStatement(BoundGotoStatement original, BoundStatement rewritten) { return InstrumentStatement(original, rewritten); }
public virtual BoundStatement InstrumentGotoStatement(BoundGotoStatement original, BoundStatement rewritten) { return(InstrumentStatement(original, rewritten)); }
public override BoundStatement InstrumentGotoStatement(BoundGotoStatement original, BoundStatement rewritten) { return(AddDynamicAnalysis(original, base.InstrumentGotoStatement(original, rewritten))); }
private BoundStatement RewriteForStatement( BoundLoopStatement original, ImmutableArray <LocalSymbol> outerLocals, BoundStatement rewrittenInitializer, BoundExpression rewrittenCondition, BoundStatement rewrittenIncrement, BoundStatement rewrittenBody, GeneratedLabelSymbol breakLabel, GeneratedLabelSymbol continueLabel, bool hasErrors) { Debug.Assert(original.Kind == BoundKind.ForStatement || original.Kind == BoundKind.ForEachStatement); Debug.Assert(rewrittenBody != null); // The sequence point behavior exhibited here is different from that of the native compiler. In the native // compiler, if you have something like // // for([|int i = 0, j = 0|]; ; [|i++, j++|]) // // then all the initializers are treated as a single sequence point, as are // all the loop incrementors. // // We now make each one individually a sequence point: // // for([|int i = 0|], [|j = 0|]; ; [|i++|], [|j++|]) // // If we decide that we want to preserve the native compiler stepping behavior // then we'll need to be a bit fancy here. The initializer and increment statements // can contain lambdas whose bodies need to have sequence points inserted, so we // need to make sure we visit the children. But we'll also need to make sure that // we do not generate one sequence point for each statement in the initializers // and the incrementors. CSharpSyntaxNode syntax = original.Syntax; var statementBuilder = ArrayBuilder <BoundStatement> .GetInstance(); if (rewrittenInitializer != null) { statementBuilder.Add(rewrittenInitializer); } var startLabel = new GeneratedLabelSymbol("start"); // for (initializer; condition; increment) // body; // // becomes the following (with block added for locals) // // { // initializer; // goto end; // start: // body; // continue: // increment; // end: // GotoIfTrue condition start; // break: // } var endLabel = new GeneratedLabelSymbol("end"); // initializer; // goto end; BoundStatement gotoEnd = new BoundGotoStatement(syntax, endLabel); if (this.Instrument) { switch (original.Kind) { case BoundKind.ForEachStatement: gotoEnd = _instrumenter.InstrumentForEachStatementGotoEnd((BoundForEachStatement)original, gotoEnd); break; case BoundKind.ForStatement: gotoEnd = _instrumenter.InstrumentForStatementGotoEnd((BoundForStatement)original, gotoEnd); break; default: throw ExceptionUtilities.UnexpectedValue(original.Kind); } } statementBuilder.Add(gotoEnd); // start: // body; statementBuilder.Add(new BoundLabelStatement(syntax, startLabel)); statementBuilder.Add(rewrittenBody); // continue: // increment; statementBuilder.Add(new BoundLabelStatement(syntax, continueLabel)); if (rewrittenIncrement != null) { statementBuilder.Add(rewrittenIncrement); } // end: // GotoIfTrue condition start; statementBuilder.Add(new BoundLabelStatement(syntax, endLabel)); BoundStatement branchBack = null; if (rewrittenCondition != null) { branchBack = new BoundConditionalGoto(rewrittenCondition.Syntax, rewrittenCondition, true, startLabel); } else { branchBack = new BoundGotoStatement(syntax, startLabel); } if (this.Instrument) { switch (original.Kind) { case BoundKind.ForEachStatement: branchBack = _instrumenter.InstrumentForEachStatementConditionalGotoStart((BoundForEachStatement)original, branchBack); break; case BoundKind.ForStatement: branchBack = _instrumenter.InstrumentForStatementConditionalGotoStart((BoundForStatement)original, branchBack); break; default: throw ExceptionUtilities.UnexpectedValue(original.Kind); } } statementBuilder.Add(branchBack); // break: statementBuilder.Add(new BoundLabelStatement(syntax, breakLabel)); var statements = statementBuilder.ToImmutableAndFree(); return(new BoundBlock(syntax, outerLocals, ImmutableArray <LocalFunctionSymbol> .Empty, statements, hasErrors)); }
public override BoundStatement InstrumentGotoStatement(BoundGotoStatement original, BoundStatement rewritten) { return(AddSequencePoint(base.InstrumentGotoStatement(original, rewritten))); }
public override BoundNode VisitBreakStatement(BoundBreakStatement node) { var result = new BoundGotoStatement(node.Syntax, node.Label, node.HasErrors); return(AddSequencePoint(result)); }
public override BoundStatement InstrumentGotoStatement(BoundGotoStatement original, BoundStatement rewritten) { return(Previous.InstrumentGotoStatement(original, rewritten)); }