Browse Source

YieldReturnDecompiler: in ConstructExceptionTable(), avoid ToEnclosingInterval() call and use the full StateRange instead.

This is necessary because the state indices for a given try block are no longer contiguous in Roslyn-compiled code.
Closes #468.
pull/469/merge
Daniel Grunwald 12 years ago
parent
commit
1bf31f9086
  1. 8
      ICSharpCode.Decompiler/ILAst/StateRange.cs
  2. 18
      ICSharpCode.Decompiler/ILAst/YieldReturnDecompiler.cs

8
ICSharpCode.Decompiler/ILAst/StateRange.cs

@ -137,7 +137,7 @@ namespace ICSharpCode.Decompiler.ILAst
internal DefaultDictionary<ILNode, StateRange> ranges; internal DefaultDictionary<ILNode, StateRange> ranges;
SymbolicEvaluationContext evalContext; SymbolicEvaluationContext evalContext;
internal Dictionary<MethodDefinition, Interval> finallyMethodToStateInterval; // used only for IteratorDispose internal Dictionary<MethodDefinition, StateRange> finallyMethodToStateRange; // used only for IteratorDispose
/// <summary> /// <summary>
/// Initializes the state range logic: /// Initializes the state range logic:
@ -148,7 +148,7 @@ namespace ICSharpCode.Decompiler.ILAst
this.mode = mode; this.mode = mode;
this.stateField = stateField; this.stateField = stateField;
if (mode == StateRangeAnalysisMode.IteratorDispose) { if (mode == StateRangeAnalysisMode.IteratorDispose) {
finallyMethodToStateInterval = new Dictionary<MethodDefinition, Interval>(); finallyMethodToStateRange = new Dictionary<MethodDefinition, StateRange>();
} }
ranges = new DefaultDictionary<ILNode, StateRange>(n => new StateRange()); ranges = new DefaultDictionary<ILNode, StateRange>(n => new StateRange());
@ -249,9 +249,9 @@ namespace ICSharpCode.Decompiler.ILAst
// in some cases (e.g. foreach over array) the C# compiler produces a finally method outside of try-finally blocks // in some cases (e.g. foreach over array) the C# compiler produces a finally method outside of try-finally blocks
if (mode == StateRangeAnalysisMode.IteratorDispose) { if (mode == StateRangeAnalysisMode.IteratorDispose) {
MethodDefinition mdef = (expr.Operand as MethodReference).ResolveWithinSameModule(); MethodDefinition mdef = (expr.Operand as MethodReference).ResolveWithinSameModule();
if (mdef == null || finallyMethodToStateInterval.ContainsKey(mdef)) if (mdef == null || finallyMethodToStateRange.ContainsKey(mdef))
throw new SymbolicAnalysisFailedException(); throw new SymbolicAnalysisFailedException();
finallyMethodToStateInterval.Add(mdef, nodeRange.ToEnclosingInterval()); finallyMethodToStateRange.Add(mdef, nodeRange);
break; break;
} else { } else {
goto default; goto default;

18
ICSharpCode.Decompiler/ILAst/YieldReturnDecompiler.cs

@ -312,7 +312,7 @@ namespace ICSharpCode.Decompiler.ILAst
// This is (int.MinValue, int.MaxValue) for the first instruction. // This is (int.MinValue, int.MaxValue) for the first instruction.
// These ranges are propagated depending on the conditional jumps performed by the code. // These ranges are propagated depending on the conditional jumps performed by the code.
Dictionary<MethodDefinition, Interval> finallyMethodToStateInterval; Dictionary<MethodDefinition, StateRange> finallyMethodToStateRange;
void ConstructExceptionTable() void ConstructExceptionTable()
{ {
@ -321,11 +321,11 @@ namespace ICSharpCode.Decompiler.ILAst
var rangeAnalysis = new StateRangeAnalysis(ilMethod.Body[0], StateRangeAnalysisMode.IteratorDispose, stateField); var rangeAnalysis = new StateRangeAnalysis(ilMethod.Body[0], StateRangeAnalysisMode.IteratorDispose, stateField);
rangeAnalysis.AssignStateRanges(ilMethod.Body, ilMethod.Body.Count); rangeAnalysis.AssignStateRanges(ilMethod.Body, ilMethod.Body.Count);
finallyMethodToStateInterval = rangeAnalysis.finallyMethodToStateInterval; finallyMethodToStateRange = rangeAnalysis.finallyMethodToStateRange;
// Now look at the finally blocks: // Now look at the finally blocks:
foreach (var tryFinally in ilMethod.GetSelfAndChildrenRecursive<ILTryCatchBlock>()) { foreach (var tryFinally in ilMethod.GetSelfAndChildrenRecursive<ILTryCatchBlock>()) {
Interval interval = rangeAnalysis.ranges[tryFinally.TryBlock.Body[0]].ToEnclosingInterval(); var range = rangeAnalysis.ranges[tryFinally.TryBlock.Body[0]];
var finallyBody = tryFinally.FinallyBlock.Body; var finallyBody = tryFinally.FinallyBlock.Body;
if (finallyBody.Count != 2) if (finallyBody.Count != 2)
throw new SymbolicAnalysisFailedException(); throw new SymbolicAnalysisFailedException();
@ -338,9 +338,9 @@ namespace ICSharpCode.Decompiler.ILAst
throw new SymbolicAnalysisFailedException(); throw new SymbolicAnalysisFailedException();
MethodDefinition mdef = GetMethodDefinition(call.Operand as MethodReference); MethodDefinition mdef = GetMethodDefinition(call.Operand as MethodReference);
if (mdef == null || finallyMethodToStateInterval.ContainsKey(mdef)) if (mdef == null || finallyMethodToStateRange.ContainsKey(mdef))
throw new SymbolicAnalysisFailedException(); throw new SymbolicAnalysisFailedException();
finallyMethodToStateInterval.Add(mdef, interval); finallyMethodToStateRange.Add(mdef, range);
} }
rangeAnalysis = null; rangeAnalysis = null;
} }
@ -507,21 +507,21 @@ namespace ICSharpCode.Decompiler.ILAst
MethodDefinition method = GetMethodDefinition(expr.Operand as MethodReference); MethodDefinition method = GetMethodDefinition(expr.Operand as MethodReference);
if (method == null) if (method == null)
throw new SymbolicAnalysisFailedException(); throw new SymbolicAnalysisFailedException();
Interval interval; StateRange stateRange;
if (method == disposeMethod) { if (method == disposeMethod) {
// Explicit call to dispose is used for "yield break;" within the method. // Explicit call to dispose is used for "yield break;" within the method.
ILExpression br = body.ElementAtOrDefault(++pos) as ILExpression; ILExpression br = body.ElementAtOrDefault(++pos) as ILExpression;
if (br == null || !(br.Code == ILCode.Br || br.Code == ILCode.Leave) || br.Operand != returnFalseLabel) if (br == null || !(br.Code == ILCode.Br || br.Code == ILCode.Leave) || br.Operand != returnFalseLabel)
throw new SymbolicAnalysisFailedException(); throw new SymbolicAnalysisFailedException();
newBody.Add(MakeGoTo(returnFalseLabel)); newBody.Add(MakeGoTo(returnFalseLabel));
} else if (finallyMethodToStateInterval.TryGetValue(method, out interval)) { } else if (finallyMethodToStateRange.TryGetValue(method, out stateRange)) {
// Call to Finally-method // Call to Finally-method
int index = stateChanges.FindIndex(ss => ss.NewState >= interval.Start && ss.NewState <= interval.End); int index = stateChanges.FindIndex(ss => stateRange.Contains(ss.NewState));
if (index < 0) if (index < 0)
throw new SymbolicAnalysisFailedException(); throw new SymbolicAnalysisFailedException();
ILLabel label = new ILLabel(); ILLabel label = new ILLabel();
label.Name = "JumpOutOfTryFinally" + interval.Start + "_" + interval.End; label.Name = "JumpOutOfTryFinally" + stateChanges[index].NewState;
newBody.Add(new ILExpression(ILCode.Leave, label)); newBody.Add(new ILExpression(ILCode.Leave, label));
SetState stateChange = stateChanges[index]; SetState stateChange = stateChanges[index];

Loading…
Cancel
Save