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 VisitDoStatement(BoundDoStatement node)
{
Debug.Assert(node != null);
var rewrittenCondition = (BoundExpression)Visit(node.Condition);
var rewrittenBody = (BoundStatement)Visit(node.Body);
var startLabel = new GeneratedLabelSymbol("start");
var syntax = node.Syntax;
// EnC: We need to insert a hidden sequence point to handle function remapping in case
// the containing method is edited while methods invoked in the condition are being executed.
if (!node.WasCompilerGenerated && this.Instrument)
{
rewrittenCondition = _instrumenter.InstrumentDoStatementCondition(node, rewrittenCondition, _factory);
}
BoundStatement ifConditionGotoStart = new BoundConditionalGoto(syntax, rewrittenCondition, true, startLabel);
if (!node.WasCompilerGenerated && this.Instrument)
{
ifConditionGotoStart = _instrumenter.InstrumentDoStatementConditionalGotoStart(node, ifConditionGotoStart);
}
// do
// body
// while (condition);
//
// becomes
//
// start:
// {
// body
// continue:
// sequence point
// GotoIfTrue condition start;
// }
// break:
if (node.Locals.IsEmpty)
{
return BoundStatementList.Synthesized(syntax, node.HasErrors,
new BoundLabelStatement(syntax, startLabel),
rewrittenBody,
new BoundLabelStatement(syntax, node.ContinueLabel),
ifConditionGotoStart,
new BoundLabelStatement(syntax, node.BreakLabel));
}
return BoundStatementList.Synthesized(syntax, node.HasErrors,
new BoundLabelStatement(syntax, startLabel),
new BoundBlock(syntax,
node.Locals,
ImmutableArray.Create<BoundStatement>(rewrittenBody,
new BoundLabelStatement(syntax, node.ContinueLabel),
ifConditionGotoStart)),
new BoundLabelStatement(syntax, node.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 VisitDoStatement(BoundDoStatement node)
{
Debug.Assert(node != null);
var rewrittenCondition = (BoundExpression)Visit(node.Condition);
var rewrittenBody = (BoundStatement)Visit(node.Body);
var startLabel = new GeneratedLabelSymbol("start");
var syntax = node.Syntax;
// EnC: We need to insert a hidden sequence point to handle function remapping in case
// the containing method is edited while methods invoked in the condition are being executed.
BoundStatement ifConditionGotoStart = new BoundConditionalGoto(syntax, AddConditionSequencePoint(rewrittenCondition, node), true, startLabel);
if (this.GenerateDebugInfo)
{
var doSyntax = (DoStatementSyntax)syntax;
var span = TextSpan.FromBounds(
doSyntax.WhileKeyword.SpanStart,
doSyntax.SemicolonToken.Span.End);
ifConditionGotoStart = new BoundSequencePointWithSpan(doSyntax, ifConditionGotoStart, span);
}
// do
// body
// while (condition);
//
// becomes
//
// start:
// {
// body
// continue:
// sequence point
// GotoIfTrue condition start;
// }
// break:
return BoundStatementList.Synthesized(syntax, node.HasErrors,
new BoundLabelStatement(syntax, startLabel),
rewrittenBody,
new BoundLabelStatement(syntax, node.ContinueLabel),
ifConditionGotoStart,
new BoundLabelStatement(syntax, node.BreakLabel));
}
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));
//.........这里部分代码省略.........
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);
//.........这里部分代码省略.........
请发表评论