1
ļ»æ// Copyright (c) 2012 AlphaSierraPapa for the SharpDevelop Team
3
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
4
// software and associated documentation files (the "Software"), to deal in the Software
5
// without restriction, including without limitation the rights to use, copy, modify, merge,
6
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
7
// to whom the Software is furnished to do so, subject to the following conditions:
9
// The above copyright notice and this permission notice shall be included in all copies or
10
// substantial portions of the Software.
12
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
13
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
15
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
16
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
17
// DEALINGS IN THE SOFTWARE.
20
using System.Collections.Generic;
21
using System.Diagnostics;
25
namespace ICSharpCode.Decompiler.ILAst
29
public readonly int Start, End;
31
public Interval(int start, int end)
33
Debug.Assert(start <= end || (start == 0 && end == -1));
38
public override string ToString()
40
return string.Format("({0} to {1})", Start, End);
46
readonly List<Interval> data = new List<Interval>();
52
public StateRange(int start, int end)
54
this.data.Add(new Interval(start, end));
58
get { return data.Count == 0; }
61
public bool Contains(int val)
63
foreach (Interval v in data) {
64
if (v.Start <= val && val <= v.End)
70
public void UnionWith(StateRange other)
72
data.AddRange(other.data);
76
/// Unions this state range with (other intersect (minVal to maxVal))
78
public void UnionWith(StateRange other, int minVal, int maxVal)
80
foreach (Interval v in other.data) {
81
int start = Math.Max(v.Start, minVal);
82
int end = Math.Min(v.End, maxVal);
84
data.Add(new Interval(start, end));
89
/// Merges overlapping interval ranges.
91
public void Simplify()
95
data.Sort((a, b) => a.Start.CompareTo(b.Start));
96
Interval prev = data[0];
98
for (int i = 1; i < data.Count; i++) {
99
Interval next = data[i];
100
Debug.Assert(prev.Start <= next.Start);
101
if (next.Start <= prev.End + 1) { // intervals overlapping or touching
102
prev = new Interval(prev.Start, Math.Max(prev.End, next.End));
103
data[prevIndex] = prev;
104
data[i] = new Interval(0, -1); // mark as deleted
110
data.RemoveAll(i => i.Start > i.End); // remove all entries that were marked as deleted
113
public override string ToString()
115
return string.Join(",", data);
118
public Interval ToEnclosingInterval()
121
throw new SymbolicAnalysisFailedException();
122
return new Interval(data[0].Start, data[data.Count - 1].End);
126
enum StateRangeAnalysisMode
133
class StateRangeAnalysis
135
readonly StateRangeAnalysisMode mode;
136
readonly FieldDefinition stateField;
137
internal DefaultDictionary<ILNode, StateRange> ranges;
138
SymbolicEvaluationContext evalContext;
140
internal Dictionary<MethodDefinition, Interval> finallyMethodToStateInterval; // used only for IteratorDispose
143
/// Initializes the state range logic:
144
/// Clears 'ranges' and sets 'ranges[entryPoint]' to the full range (int.MinValue to int.MaxValue)
146
public StateRangeAnalysis(ILNode entryPoint, StateRangeAnalysisMode mode, FieldDefinition stateField)
149
this.stateField = stateField;
150
if (mode == StateRangeAnalysisMode.IteratorDispose) {
151
finallyMethodToStateInterval = new Dictionary<MethodDefinition, Interval>();
154
ranges = new DefaultDictionary<ILNode, StateRange>(n => new StateRange());
155
ranges[entryPoint] = new StateRange(int.MinValue, int.MaxValue);
156
evalContext = new SymbolicEvaluationContext(stateField);
159
public int AssignStateRanges(List<ILNode> body, int bodyLength)
163
for (int i = 0; i < bodyLength; i++) {
164
StateRange nodeRange = ranges[body[i]];
165
nodeRange.Simplify();
167
ILLabel label = body[i] as ILLabel;
169
ranges[body[i + 1]].UnionWith(nodeRange);
173
ILTryCatchBlock tryFinally = body[i] as ILTryCatchBlock;
174
if (tryFinally != null) {
175
if (mode == StateRangeAnalysisMode.IteratorDispose) {
176
if (tryFinally.CatchBlocks.Count != 0 || tryFinally.FaultBlock != null || tryFinally.FinallyBlock == null)
177
throw new SymbolicAnalysisFailedException();
178
ranges[tryFinally.TryBlock].UnionWith(nodeRange);
179
if (tryFinally.TryBlock.Body.Count != 0) {
180
ranges[tryFinally.TryBlock.Body[0]].UnionWith(nodeRange);
181
AssignStateRanges(tryFinally.TryBlock.Body, tryFinally.TryBlock.Body.Count);
184
} else if (mode == StateRangeAnalysisMode.AsyncMoveNext) {
187
throw new SymbolicAnalysisFailedException();
191
ILExpression expr = body[i] as ILExpression;
193
throw new SymbolicAnalysisFailedException();
197
SymbolicValue val = evalContext.Eval(expr.Arguments[0]);
198
if (val.Type != SymbolicValueType.State)
200
ILLabel[] targetLabels = (ILLabel[])expr.Operand;
201
for (int j = 0; j < targetLabels.Length; j++) {
202
int state = j - val.Constant;
203
ranges[targetLabels[j]].UnionWith(nodeRange, state, state);
205
StateRange nextRange = ranges[body[i + 1]];
206
nextRange.UnionWith(nodeRange, int.MinValue, -1 - val.Constant);
207
nextRange.UnionWith(nodeRange, targetLabels.Length - val.Constant, int.MaxValue);
212
ranges[(ILLabel)expr.Operand].UnionWith(nodeRange);
216
SymbolicValue val = evalContext.Eval(expr.Arguments[0]);
217
if (val.Type == SymbolicValueType.StateEquals) {
218
ranges[(ILLabel)expr.Operand].UnionWith(nodeRange, val.Constant, val.Constant);
219
StateRange nextRange = ranges[body[i + 1]];
220
nextRange.UnionWith(nodeRange, int.MinValue, val.Constant - 1);
221
nextRange.UnionWith(nodeRange, val.Constant + 1, int.MaxValue);
223
} else if (val.Type == SymbolicValueType.StateInEquals) {
224
ranges[body[i + 1]].UnionWith(nodeRange, val.Constant, val.Constant);
225
StateRange targetRange = ranges[(ILLabel)expr.Operand];
226
targetRange.UnionWith(nodeRange, int.MinValue, val.Constant - 1);
227
targetRange.UnionWith(nodeRange, val.Constant + 1, int.MaxValue);
234
ranges[body[i + 1]].UnionWith(nodeRange);
240
SymbolicValue val = evalContext.Eval(expr.Arguments[0]);
241
if (val.Type == SymbolicValueType.State && val.Constant == 0) {
242
evalContext.AddStateVariable((ILVariable)expr.Operand);
243
goto case ILCode.Nop;
249
// in some cases (e.g. foreach over array) the C# compiler produces a finally method outside of try-finally blocks
250
if (mode == StateRangeAnalysisMode.IteratorDispose) {
251
MethodDefinition mdef = (expr.Operand as MethodReference).ResolveWithinSameModule();
252
if (mdef == null || finallyMethodToStateInterval.ContainsKey(mdef))
253
throw new SymbolicAnalysisFailedException();
254
finallyMethodToStateInterval.Add(mdef, nodeRange.ToEnclosingInterval());
260
if (mode == StateRangeAnalysisMode.IteratorDispose) {
261
throw new SymbolicAnalysisFailedException();
270
public void EnsureLabelAtPos(List<ILNode> body, ref int pos, ref int bodyLength)
272
if (pos > 0 && body[pos - 1] is ILLabel) {
275
// ensure that the first element at body[pos] is a label:
276
ILLabel newLabel = new ILLabel();
277
newLabel.Name = "YieldReturnEntryPoint";
278
ranges[newLabel] = ranges[body[pos]]; // give the label the range of the instruction at body[pos]
279
body.Insert(pos, newLabel);
284
public LabelRangeMapping CreateLabelRangeMapping(List<ILNode> body, int pos, int bodyLength)
286
LabelRangeMapping result = new LabelRangeMapping();
287
CreateLabelRangeMapping(body, pos, bodyLength, result, false);
291
void CreateLabelRangeMapping(List<ILNode> body, int pos, int bodyLength, LabelRangeMapping result, bool onlyInitialLabels)
293
for (int i = pos; i < bodyLength; i++) {
294
ILLabel label = body[i] as ILLabel;
296
result.Add(new KeyValuePair<ILLabel, StateRange>(label, ranges[label]));
298
ILTryCatchBlock tryCatchBlock = body[i] as ILTryCatchBlock;
299
if (tryCatchBlock != null) {
300
CreateLabelRangeMapping(tryCatchBlock.TryBlock.Body, 0, tryCatchBlock.TryBlock.Body.Count, result, true);
301
} else if (onlyInitialLabels) {
309
class LabelRangeMapping : List<KeyValuePair<ILLabel, StateRange>> {}