-
Notifications
You must be signed in to change notification settings - Fork 4k
/
LocalRewriter_WhileStatement.cs
157 lines (137 loc) · 6.43 KB
/
LocalRewriter_WhileStatement.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Diagnostics;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
using System.Collections.Immutable;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CSharp
{
internal sealed partial class LocalRewriter
{
public override BoundNode VisitWhileStatement(BoundWhileStatement node)
{
Debug.Assert(node != null);
var rewrittenCondition = VisitExpression(node.Condition);
var rewrittenBody = VisitStatement(node.Body);
Debug.Assert(rewrittenBody is { });
// 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.InstrumentWhileStatementCondition(node, rewrittenCondition, _factory);
}
return RewriteWhileStatement(
node,
node.Locals,
rewrittenCondition,
rewrittenBody,
node.BreakLabel,
node.ContinueLabel,
node.HasErrors);
}
private BoundStatement RewriteWhileStatement(
BoundNode loop,
BoundExpression rewrittenCondition,
BoundStatement rewrittenBody,
GeneratedLabelSymbol breakLabel,
GeneratedLabelSymbol continueLabel,
bool hasErrors)
{
Debug.Assert(loop.Kind is BoundKind.WhileStatement or BoundKind.ForEachStatement or BoundKind.CollectionExpressionSpreadElement);
// 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;
case BoundKind.CollectionExpressionSpreadElement:
// No instrumentation needed since the loop for the spread expression
// was generated in lowering, and not explicit in the source.
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 = BoundSequencePoint.CreateHidden(gotoContinue);
}
return BoundStatementList.Synthesized(syntax, hasErrors,
gotoContinue,
new BoundLabelStatement(syntax, startLabel),
rewrittenBody,
new BoundLabelStatement(syntax, continueLabel),
ifConditionGotoStart,
new BoundLabelStatement(syntax, breakLabel));
}
private BoundStatement RewriteWhileStatement(
BoundWhileStatement loop,
ImmutableArray<LocalSymbol> locals,
BoundExpression rewrittenCondition,
BoundStatement rewrittenBody,
GeneratedLabelSymbol breakLabel,
GeneratedLabelSymbol continueLabel,
bool hasErrors)
{
if (locals.IsEmpty)
{
return RewriteWhileStatement(loop, rewrittenCondition, rewrittenBody, breakLabel, continueLabel, hasErrors);
}
// We need to enter scope-block from the top, that is where an instance of a display class will be created
// if any local is captured within a lambda.
// while (condition)
// body;
//
// becomes
//
// continue:
// {
// GotoIfFalse condition break;
// body
// goto continue;
// }
// break:
SyntaxNode syntax = loop.Syntax;
BoundStatement continueLabelStatement = new BoundLabelStatement(syntax, continueLabel);
BoundStatement ifNotConditionGotoBreak = new BoundConditionalGoto(rewrittenCondition.Syntax, rewrittenCondition, false, breakLabel);
if (this.Instrument && !loop.WasCompilerGenerated)
{
ifNotConditionGotoBreak = Instrumenter.InstrumentWhileStatementConditionalGotoStartOrBreak(loop, ifNotConditionGotoBreak);
continueLabelStatement = BoundSequencePoint.CreateHidden(continueLabelStatement);
}
return BoundStatementList.Synthesized(syntax, hasErrors,
continueLabelStatement,
new BoundBlock(syntax,
locals,
ImmutableArray.Create(
ifNotConditionGotoBreak,
rewrittenBody,
new BoundGotoStatement(syntax, continueLabel))),
new BoundLabelStatement(syntax, breakLabel));
}
}
}