322
319
disposeMethod = enumeratorType.Methods.FirstOrDefault(m => m.Name == "System.IDisposable.Dispose");
323
320
ILBlock ilMethod = CreateILAst(disposeMethod);
325
finallyMethodToStateInterval = new Dictionary<MethodDefinition, Interval>();
327
InitStateRanges(ilMethod.Body[0]);
328
AssignStateRanges(ilMethod.Body, ilMethod.Body.Count, forDispose: true);
322
var rangeAnalysis = new StateRangeAnalysis(ilMethod.Body[0], StateRangeAnalysisMode.IteratorDispose, stateField);
323
rangeAnalysis.AssignStateRanges(ilMethod.Body, ilMethod.Body.Count);
324
finallyMethodToStateInterval = rangeAnalysis.finallyMethodToStateInterval;
330
326
// Now look at the finally blocks:
331
327
foreach (var tryFinally in ilMethod.GetSelfAndChildrenRecursive<ILTryCatchBlock>()) {
332
Interval interval = ranges[tryFinally.TryBlock.Body[0]].ToEnclosingInterval();
328
Interval interval = rangeAnalysis.ranges[tryFinally.TryBlock.Body[0]].ToEnclosingInterval();
333
329
var finallyBody = tryFinally.FinallyBlock.Body;
334
330
if (finallyBody.Count != 2)
335
throw new YieldAnalysisFailedException();
331
throw new SymbolicAnalysisFailedException();
336
332
ILExpression call = finallyBody[0] as ILExpression;
337
333
if (call == null || call.Code != ILCode.Call || call.Arguments.Count != 1)
338
throw new YieldAnalysisFailedException();
334
throw new SymbolicAnalysisFailedException();
339
335
if (!call.Arguments[0].MatchThis())
340
throw new YieldAnalysisFailedException();
336
throw new SymbolicAnalysisFailedException();
341
337
if (!finallyBody[1].Match(ILCode.Endfinally))
342
throw new YieldAnalysisFailedException();
338
throw new SymbolicAnalysisFailedException();
344
340
MethodDefinition mdef = GetMethodDefinition(call.Operand as MethodReference);
345
341
if (mdef == null || finallyMethodToStateInterval.ContainsKey(mdef))
346
throw new YieldAnalysisFailedException();
342
throw new SymbolicAnalysisFailedException();
347
343
finallyMethodToStateInterval.Add(mdef, interval);
353
#region Assign StateRanges / Symbolic Execution (used for analysis of Dispose() and MoveNext())
354
#region struct Interval / class StateRange
357
public readonly int Start, End;
359
public Interval(int start, int end)
361
Debug.Assert(start <= end || (start == 0 && end == -1));
366
public override string ToString()
368
return string.Format("({0} to {1})", Start, End);
374
readonly List<Interval> data = new List<Interval>();
380
public StateRange(int start, int end)
382
this.data.Add(new Interval(start, end));
385
public bool Contains(int val)
387
foreach (Interval v in data) {
388
if (v.Start <= val && val <= v.End)
394
public void UnionWith(StateRange other)
396
data.AddRange(other.data);
400
/// Unions this state range with (other intersect (minVal to maxVal))
402
public void UnionWith(StateRange other, int minVal, int maxVal)
404
foreach (Interval v in other.data) {
405
int start = Math.Max(v.Start, minVal);
406
int end = Math.Min(v.End, maxVal);
408
data.Add(new Interval(start, end));
413
/// Merges overlapping interval ranges.
415
public void Simplify()
419
data.Sort((a, b) => a.Start.CompareTo(b.Start));
420
Interval prev = data[0];
422
for (int i = 1; i < data.Count; i++) {
423
Interval next = data[i];
424
Debug.Assert(prev.Start <= next.Start);
425
if (next.Start <= prev.End + 1) { // intervals overlapping or touching
426
prev = new Interval(prev.Start, Math.Max(prev.End, next.End));
427
data[prevIndex] = prev;
428
data[i] = new Interval(0, -1); // mark as deleted
434
data.RemoveAll(i => i.Start > i.End); // remove all entries that were marked as deleted
437
public override string ToString()
439
return string.Join(",", data);
442
public Interval ToEnclosingInterval()
445
throw new YieldAnalysisFailedException();
446
return new Interval(data[0].Start, data[data.Count - 1].End);
451
DefaultDictionary<ILNode, StateRange> ranges;
452
ILVariable rangeAnalysisStateVariable;
455
/// Initializes the state range logic:
456
/// Clears 'ranges' and sets 'ranges[entryPoint]' to the full range (int.MinValue to int.MaxValue)
458
void InitStateRanges(ILNode entryPoint)
460
ranges = new DefaultDictionary<ILNode, StateRange>(n => new StateRange());
461
ranges[entryPoint] = new StateRange(int.MinValue, int.MaxValue);
462
rangeAnalysisStateVariable = null;
465
int AssignStateRanges(List<ILNode> body, int bodyLength, bool forDispose)
469
for (int i = 0; i < bodyLength; i++) {
470
StateRange nodeRange = ranges[body[i]];
471
nodeRange.Simplify();
473
ILLabel label = body[i] as ILLabel;
475
ranges[body[i + 1]].UnionWith(nodeRange);
479
ILTryCatchBlock tryFinally = body[i] as ILTryCatchBlock;
480
if (tryFinally != null) {
481
if (!forDispose || tryFinally.CatchBlocks.Count != 0 || tryFinally.FaultBlock != null || tryFinally.FinallyBlock == null)
482
throw new YieldAnalysisFailedException();
483
ranges[tryFinally.TryBlock].UnionWith(nodeRange);
484
if (tryFinally.TryBlock.Body.Count != 0) {
485
ranges[tryFinally.TryBlock.Body[0]].UnionWith(nodeRange);
486
AssignStateRanges(tryFinally.TryBlock.Body, tryFinally.TryBlock.Body.Count, forDispose);
491
ILExpression expr = body[i] as ILExpression;
493
throw new YieldAnalysisFailedException();
497
SymbolicValue val = Eval(expr.Arguments[0]);
498
if (val.Type != SymbolicValueType.State)
499
throw new YieldAnalysisFailedException();
500
ILLabel[] targetLabels = (ILLabel[])expr.Operand;
501
for (int j = 0; j < targetLabels.Length; j++) {
502
int state = j - val.Constant;
503
ranges[targetLabels[j]].UnionWith(nodeRange, state, state);
505
StateRange nextRange = ranges[body[i + 1]];
506
nextRange.UnionWith(nodeRange, int.MinValue, -1 - val.Constant);
507
nextRange.UnionWith(nodeRange, targetLabels.Length - val.Constant, int.MaxValue);
512
ranges[(ILLabel)expr.Operand].UnionWith(nodeRange);
516
SymbolicValue val = Eval(expr.Arguments[0]);
517
if (val.Type == SymbolicValueType.StateEquals) {
518
ranges[(ILLabel)expr.Operand].UnionWith(nodeRange, val.Constant, val.Constant);
519
StateRange nextRange = ranges[body[i + 1]];
520
nextRange.UnionWith(nodeRange, int.MinValue, val.Constant - 1);
521
nextRange.UnionWith(nodeRange, val.Constant + 1, int.MaxValue);
522
} else if (val.Type == SymbolicValueType.StateInEquals) {
523
ranges[body[i + 1]].UnionWith(nodeRange, val.Constant, val.Constant);
524
StateRange targetRange = ranges[(ILLabel)expr.Operand];
525
targetRange.UnionWith(nodeRange, int.MinValue, val.Constant - 1);
526
targetRange.UnionWith(nodeRange, val.Constant + 1, int.MaxValue);
528
throw new YieldAnalysisFailedException();
533
ranges[body[i + 1]].UnionWith(nodeRange);
539
SymbolicValue val = Eval(expr.Arguments[0]);
540
if (val.Type == SymbolicValueType.State && val.Constant == 0 && rangeAnalysisStateVariable == null)
541
rangeAnalysisStateVariable = (ILVariable)expr.Operand;
543
throw new YieldAnalysisFailedException();
544
goto case ILCode.Nop;
547
// in some cases (e.g. foreach over array) the C# compiler produces a finally method outside of try-finally blocks
549
MethodDefinition mdef = GetMethodDefinition(expr.Operand as MethodReference);
550
if (mdef == null || finallyMethodToStateInterval.ContainsKey(mdef))
551
throw new YieldAnalysisFailedException();
552
finallyMethodToStateInterval.Add(mdef, nodeRange.ToEnclosingInterval());
554
throw new YieldAnalysisFailedException();
559
throw new YieldAnalysisFailedException();
567
enum SymbolicValueType
570
/// int: Constant (result of ldc.i4)
574
/// int: State + Constant
578
/// This pointer (result of ldarg.0)
582
/// bool: State == Constant
586
/// bool: State != Constant
593
public readonly int Constant;
594
public readonly SymbolicValueType Type;
596
public SymbolicValue(SymbolicValueType type, int constant = 0)
599
this.Constant = constant;
602
public override string ToString()
604
return string.Format("[SymbolicValue {0}: {1}]", this.Type, this.Constant);
608
SymbolicValue Eval(ILExpression expr)
610
SymbolicValue left, right;
613
left = Eval(expr.Arguments[0]);
614
right = Eval(expr.Arguments[1]);
615
if (left.Type != SymbolicValueType.State && left.Type != SymbolicValueType.IntegerConstant)
616
throw new YieldAnalysisFailedException();
617
if (right.Type != SymbolicValueType.IntegerConstant)
618
throw new YieldAnalysisFailedException();
619
return new SymbolicValue(left.Type, unchecked ( left.Constant - right.Constant ));
621
if (Eval(expr.Arguments[0]).Type != SymbolicValueType.This)
622
throw new YieldAnalysisFailedException();
623
if (GetFieldDefinition(expr.Operand as FieldReference) != stateField)
624
throw new YieldAnalysisFailedException();
625
return new SymbolicValue(SymbolicValueType.State);
627
ILVariable loadedVariable = (ILVariable)expr.Operand;
628
if (loadedVariable == rangeAnalysisStateVariable)
629
return new SymbolicValue(SymbolicValueType.State);
630
else if (loadedVariable.IsParameter && loadedVariable.OriginalParameter.Index < 0)
631
return new SymbolicValue(SymbolicValueType.This);
633
throw new YieldAnalysisFailedException();
635
return new SymbolicValue(SymbolicValueType.IntegerConstant, (int)expr.Operand);
638
left = Eval(expr.Arguments[0]);
639
right = Eval(expr.Arguments[1]);
640
if (left.Type != SymbolicValueType.State || right.Type != SymbolicValueType.IntegerConstant)
641
throw new YieldAnalysisFailedException();
642
// bool: (state + left.Constant == right.Constant)
643
// bool: (state == right.Constant - left.Constant)
644
return new SymbolicValue(expr.Code == ILCode.Ceq ? SymbolicValueType.StateEquals : SymbolicValueType.StateInEquals, unchecked(right.Constant - left.Constant));
645
case ILCode.LogicNot:
646
SymbolicValue val = Eval(expr.Arguments[0]);
647
if (val.Type == SymbolicValueType.StateEquals)
648
return new SymbolicValue(SymbolicValueType.StateInEquals, val.Constant);
649
else if (val.Type == SymbolicValueType.StateInEquals)
650
return new SymbolicValue(SymbolicValueType.StateEquals, val.Constant);
652
throw new YieldAnalysisFailedException();
654
throw new YieldAnalysisFailedException();
345
rangeAnalysis = null;
735
425
ILExpression store0 = body.ElementAtOrDefault(bodyLength - 1) as ILExpression;
736
426
if (store0 == null || store0.Code != ILCode.Stloc || store0.Operand != returnVariable)
737
throw new YieldAnalysisFailedException();
427
throw new SymbolicAnalysisFailedException();
738
428
if (store0.Arguments[0].Code != ILCode.Ldc_I4 || (int)store0.Arguments[0].Operand != 0)
739
throw new YieldAnalysisFailedException();
429
throw new SymbolicAnalysisFailedException();
741
431
bodyLength--; // don't conside the stloc instruction to be part of the body
743
433
// verify that the last element in the body is a label pointing to the 'ret(false)'
744
434
returnFalseLabel = body.ElementAtOrDefault(bodyLength - 1) as ILLabel;
745
435
if (returnFalseLabel == null)
746
throw new YieldAnalysisFailedException();
748
InitStateRanges(body[0]);
749
int pos = AssignStateRanges(body, bodyLength, forDispose: false);
750
if (pos > 0 && body[pos - 1] is ILLabel) {
753
// ensure that the first element at body[pos] is a label:
754
ILLabel newLabel = new ILLabel();
755
newLabel.Name = "YieldReturnEntryPoint";
756
ranges[newLabel] = ranges[body[pos]]; // give the label the range of the instruction at body[pos]
758
body.Insert(pos, newLabel);
762
List<KeyValuePair<ILLabel, StateRange>> labels = new List<KeyValuePair<ILLabel, StateRange>>();
763
for (int i = pos; i < bodyLength; i++) {
764
ILLabel label = body[i] as ILLabel;
766
labels.Add(new KeyValuePair<ILLabel, StateRange>(label, ranges[label]));
436
throw new SymbolicAnalysisFailedException();
438
var rangeAnalysis = new StateRangeAnalysis(body[0], StateRangeAnalysisMode.IteratorMoveNext, stateField);
439
int pos = rangeAnalysis.AssignStateRanges(body, bodyLength);
440
rangeAnalysis.EnsureLabelAtPos(body, ref pos, ref bodyLength);
442
var labels = rangeAnalysis.CreateLabelRangeMapping(body, pos, bodyLength);
770
443
ConvertBody(body, pos, bodyLength, labels);