Browse Source

YieldReturnDecompiler: Use symbolic execution to analyze the jump table at the start of MoveNext().

pull/70/head
Daniel Grunwald 15 years ago
parent
commit
bfcc0e779b
  1. 386
      ICSharpCode.Decompiler/ILAst/YieldReturnDecompiler.cs

386
ICSharpCode.Decompiler/ILAst/YieldReturnDecompiler.cs

@ -44,24 +44,33 @@ namespace ICSharpCode.Decompiler.ILAst
if (!yrd.MatchEnumeratorCreationPattern(method)) if (!yrd.MatchEnumeratorCreationPattern(method))
return; return;
yrd.enumeratorType = yrd.enumeratorCtor.DeclaringType; yrd.enumeratorType = yrd.enumeratorCtor.DeclaringType;
#if !DEBUG #if DEBUG
try { if (Debugger.IsAttached) {
yrd.Run();
} else {
#endif #endif
yrd.AnalyzeCtor(); try {
yrd.AnalyzeCurrentProperty(); yrd.Run();
yrd.ResolveIEnumerableIEnumeratorFieldMapping(); } catch (YieldAnalysisFailedException) {
yrd.ConstructExceptionTable(); return;
yrd.AnalyzeMoveNext(); }
yrd.TranslateFieldsToLocalAccess(); #if DEBUG
#if !DEBUG
} catch (YieldAnalysisFailedException) {
return;
} }
#endif #endif
method.Body.Clear(); method.Body.Clear();
method.EntryGoto = null; method.EntryGoto = null;
method.Body.AddRange(yrd.newBody); method.Body.AddRange(yrd.newBody);
} }
void Run()
{
AnalyzeCtor();
AnalyzeCurrentProperty();
ResolveIEnumerableIEnumeratorFieldMapping();
ConstructExceptionTable();
AnalyzeMoveNext();
TranslateFieldsToLocalAccess();
}
#endregion #endregion
#region Match the enumerator creation pattern #region Match the enumerator creation pattern
@ -139,11 +148,16 @@ namespace ICSharpCode.Decompiler.ILAst
return false; return false;
if (expr.Arguments[0].Code != ILCode.Ldc_I4 || (int)expr.Arguments[0].Operand != -2) if (expr.Arguments[0].Code != ILCode.Ldc_I4 || (int)expr.Arguments[0].Operand != -2)
return false; return false;
if (ctor == null || !ctor.DeclaringType.IsCompilerGenerated()) if (ctor == null || ctor.DeclaringType.DeclaringType != context.CurrentType)
return false; return false;
if (ctor.DeclaringType.DeclaringType != context.CurrentType) return IsCompilerGeneratorEnumerator(ctor.DeclaringType);
}
public static bool IsCompilerGeneratorEnumerator(TypeDefinition type)
{
if (!(type.Name.StartsWith("<", StringComparison.Ordinal) && type.IsCompilerGenerated()))
return false; return false;
foreach (TypeReference i in ctor.DeclaringType.Interfaces) { foreach (TypeReference i in type.Interfaces) {
if (i.Namespace == "System.Collections" && i.Name == "IEnumerator") if (i.Namespace == "System.Collections" && i.Name == "IEnumerator")
return true; return true;
} }
@ -151,7 +165,7 @@ namespace ICSharpCode.Decompiler.ILAst
} }
#endregion #endregion
#region Figure out what the 'state' field is #region Figure out what the 'state' field is (analysis of .ctor())
/// <summary> /// <summary>
/// Looks at the enumerator's ctor and figures out which of the fields holds the state. /// Looks at the enumerator's ctor and figures out which of the fields holds the state.
/// </summary> /// </summary>
@ -187,7 +201,7 @@ namespace ICSharpCode.Decompiler.ILAst
} }
#endregion #endregion
#region Figure out what the 'current' field is #region Figure out what the 'current' field is (analysis of get_Current())
static readonly ILExpression returnFieldFromThisPattern = new ILExpression(ILCode.Ret, null, new ILExpression(ILCode.Ldfld, ILExpression.AnyOperand, LoadFromArgument.This)); static readonly ILExpression returnFieldFromThisPattern = new ILExpression(ILCode.Ret, null, new ILExpression(ILCode.Ldfld, ILExpression.AnyOperand, LoadFromArgument.This));
/// <summary> /// <summary>
@ -220,7 +234,7 @@ namespace ICSharpCode.Decompiler.ILAst
} }
#endregion #endregion
#region Figure out the mapping of IEnumerable fields to IEnumerator fields #region Figure out the mapping of IEnumerable fields to IEnumerator fields (analysis of GetEnumerator())
void ResolveIEnumerableIEnumeratorFieldMapping() void ResolveIEnumerableIEnumeratorFieldMapping()
{ {
MethodDefinition getEnumeratorMethod = enumeratorType.Methods.FirstOrDefault( MethodDefinition getEnumeratorMethod = enumeratorType.Methods.FirstOrDefault(
@ -249,7 +263,7 @@ namespace ICSharpCode.Decompiler.ILAst
} }
#endregion #endregion
#region Construction of the exception table #region Construction of the exception table (analysis of Dispose())
// We construct the exception table by analyzing the enumerator's Dispose() method. // We construct the exception table by analyzing the enumerator's Dispose() method.
// Assumption: there are no loops/backward jumps // Assumption: there are no loops/backward jumps
@ -259,6 +273,44 @@ 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;
void ConstructExceptionTable()
{
disposeMethod = enumeratorType.Methods.FirstOrDefault(m => m.Name == "System.IDisposable.Dispose");
ILBlock ilMethod = CreateILAst(disposeMethod);
finallyMethodToStateInterval = new Dictionary<MethodDefinition, Interval>();
InitStateRanges(ilMethod.Body[0]);
AssignStateRanges(ilMethod.Body, ilMethod.Body.Count, forDispose: true);
// Now look at the finally blocks:
foreach (var tryFinally in ilMethod.GetSelfAndChildrenRecursive<ILTryCatchBlock>()) {
Interval interval = ranges[tryFinally.TryBlock.Body[0]].ToEnclosingInterval();
var finallyBody = tryFinally.FinallyBlock.Body;
if (!(finallyBody.Count == 2 || finallyBody.Count == 3))
throw new YieldAnalysisFailedException();
ILExpression call = finallyBody[0] as ILExpression;
if (call == null || call.Code != ILCode.Call || call.Arguments.Count != 1)
throw new YieldAnalysisFailedException();
if (call.Arguments[0].Code != ILCode.Ldarg || ((ParameterDefinition)call.Arguments[0].Operand).Index >= 0)
throw new YieldAnalysisFailedException();
if (finallyBody.Count == 3 && !finallyBody[1].Match(ILCode.Nop))
throw new YieldAnalysisFailedException();
if (!finallyBody[finallyBody.Count - 1].Match(ILCode.Endfinally))
throw new YieldAnalysisFailedException();
MethodDefinition mdef = call.Operand as MethodDefinition;
if (mdef == null || finallyMethodToStateInterval.ContainsKey(mdef))
throw new YieldAnalysisFailedException();
finallyMethodToStateInterval.Add(mdef, interval);
}
ranges = null;
}
#endregion
#region Assign StateRanges / Symbolic Execution (used for analysis of Dispose() and MoveNext())
#region struct Interval / class StateRange #region struct Interval / class StateRange
struct Interval struct Interval
{ {
@ -290,6 +342,15 @@ namespace ICSharpCode.Decompiler.ILAst
this.data.Add(new Interval(start, end)); this.data.Add(new Interval(start, end));
} }
public bool Contains(int val)
{
foreach (Interval v in data) {
if (v.Start <= val && val <= v.End)
return true;
}
return false;
}
public void UnionWith(StateRange other) public void UnionWith(StateRange other)
{ {
data.AddRange(other.data); data.AddRange(other.data);
@ -338,120 +399,139 @@ namespace ICSharpCode.Decompiler.ILAst
return string.Join(",", data); return string.Join(",", data);
} }
public Interval ToInterval() public Interval ToEnclosingInterval()
{ {
if (data.Count == 1) if (data.Count == 0)
return data[0];
else
throw new YieldAnalysisFailedException(); throw new YieldAnalysisFailedException();
return new Interval(data[0].Start, data[data.Count - 1].End);
} }
} }
#endregion #endregion
DefaultDictionary<ILNode, StateRange> ranges; DefaultDictionary<ILNode, StateRange> ranges;
Dictionary<MethodDefinition, Interval> finallyMethodToStateInterval = new Dictionary<MethodDefinition, Interval>(); ILVariable rangeAnalysisStateVariable;
void ConstructExceptionTable() /// <summary>
/// Initializes the state range logic:
/// Clears 'ranges' and sets 'ranges[entryPoint]' to the full range (int.MinValue to int.MaxValue)
/// </summary>
void InitStateRanges(ILNode entryPoint)
{ {
disposeMethod = enumeratorType.Methods.FirstOrDefault(m => m.Name == "System.IDisposable.Dispose"); ranges = new DefaultDictionary<ILNode, StateRange>(n => new StateRange());
ILBlock ilMethod = CreateILAst(disposeMethod); ranges[entryPoint] = new StateRange(int.MinValue, int.MaxValue);
rangeAnalysisStateVariable = null;
ranges = new DefaultDictionary<ILNode, StateRange>(node => new StateRange());
ranges[ilMethod] = new StateRange(int.MinValue, int.MaxValue);
AssignStateRanges(ilMethod);
// Now look at the finally blocks:
foreach (var tryFinally in ilMethod.GetSelfAndChildrenRecursive<ILTryCatchBlock>()) {
Interval interval = ranges[tryFinally.TryBlock.Body[0]].ToInterval();
var finallyBody = tryFinally.FinallyBlock.Body;
if (!(finallyBody.Count == 2 || finallyBody.Count == 3))
throw new YieldAnalysisFailedException();
ILExpression call = finallyBody[0] as ILExpression;
if (call == null || call.Code != ILCode.Call || call.Arguments.Count != 1)
throw new YieldAnalysisFailedException();
if (call.Arguments[0].Code != ILCode.Ldarg || ((ParameterDefinition)call.Arguments[0].Operand).Index >= 0)
throw new YieldAnalysisFailedException();
if (finallyBody.Count == 3 && !finallyBody[1].Match(ILCode.Nop))
throw new YieldAnalysisFailedException();
if (!finallyBody[finallyBody.Count - 1].Match(ILCode.Endfinally))
throw new YieldAnalysisFailedException();
MethodDefinition mdef = call.Operand as MethodDefinition;
if (mdef == null || finallyMethodToStateInterval.ContainsKey(mdef))
throw new YieldAnalysisFailedException();
finallyMethodToStateInterval.Add(mdef, interval);
}
ranges = null;
} }
#region Assign StateRanges / Symbolic Execution int AssignStateRanges(List<ILNode> body, int bodyLength, bool forDispose)
void AssignStateRanges(ILBlock block)
{ {
if (block.Body.Count == 0) if (bodyLength == 0)
return; return 0;
ranges[block.Body[0]].UnionWith(ranges[block]); for (int i = 0; i < bodyLength; i++) {
for (int i = 0; i < block.Body.Count; i++) { StateRange nodeRange = ranges[body[i]];
StateRange nodeRange = ranges[block.Body[i]];
nodeRange.Simplify(); nodeRange.Simplify();
ILLabel label = block.Body[i] as ILLabel; ILLabel label = body[i] as ILLabel;
if (label != null) { if (label != null) {
ranges[block.Body[i + 1]].UnionWith(nodeRange); ranges[body[i + 1]].UnionWith(nodeRange);
continue; continue;
} }
ILTryCatchBlock tryFinally = block.Body[i] as ILTryCatchBlock; ILTryCatchBlock tryFinally = body[i] as ILTryCatchBlock;
if (tryFinally != null) { if (tryFinally != null) {
if (tryFinally.CatchBlocks.Count != 0 || tryFinally.FaultBlock != null || tryFinally.FinallyBlock == null) if (!forDispose || tryFinally.CatchBlocks.Count != 0 || tryFinally.FaultBlock != null || tryFinally.FinallyBlock == null)
throw new YieldAnalysisFailedException(); throw new YieldAnalysisFailedException();
ranges[tryFinally.TryBlock].UnionWith(nodeRange); ranges[tryFinally.TryBlock].UnionWith(nodeRange);
AssignStateRanges(tryFinally.TryBlock); AssignStateRanges(tryFinally.TryBlock.Body, tryFinally.TryBlock.Body.Count, forDispose);
continue; continue;
} }
ILExpression expr = block.Body[i] as ILExpression; ILExpression expr = body[i] as ILExpression;
if (expr == null) if (expr == null)
throw new YieldAnalysisFailedException(); throw new YieldAnalysisFailedException();
switch (expr.Code) { switch (expr.Code) {
case ILCode.Switch: case ILCode.Switch:
SymbolicValue val = Eval(expr.Arguments[0]); {
if (val.Type != SymbolicValueType.State) SymbolicValue val = Eval(expr.Arguments[0]);
throw new YieldAnalysisFailedException(); if (val.Type != SymbolicValueType.State)
ILLabel[] targetLabels = (ILLabel[])expr.Operand; throw new YieldAnalysisFailedException();
for (int j = 0; j < targetLabels.Length; j++) { ILLabel[] targetLabels = (ILLabel[])expr.Operand;
int state = j - val.Constant; for (int j = 0; j < targetLabels.Length; j++) {
ranges[targetLabels[j]].UnionWith(nodeRange, state, state); int state = j - val.Constant;
ranges[targetLabels[j]].UnionWith(nodeRange, state, state);
}
StateRange nextRange = ranges[body[i + 1]];
nextRange.UnionWith(nodeRange, int.MinValue, -1 - val.Constant);
nextRange.UnionWith(nodeRange, targetLabels.Length - val.Constant, int.MaxValue);
break;
} }
ranges[block.Body[i + 1]].UnionWith(nodeRange, int.MinValue, -1 - val.Constant);
ranges[block.Body[i + 1]].UnionWith(nodeRange, targetLabels.Length - val.Constant, int.MaxValue);
break;
case ILCode.Br: case ILCode.Br:
case ILCode.Leave: case ILCode.Leave:
ranges[(ILLabel)expr.Operand].UnionWith(nodeRange); ranges[(ILLabel)expr.Operand].UnionWith(nodeRange);
break; break;
case ILCode.Brtrue:
{
SymbolicValue val = Eval(expr.Arguments[0]);
if (val.Type != SymbolicValueType.StateEquals)
throw new YieldAnalysisFailedException();
ranges[(ILLabel)expr.Operand].UnionWith(nodeRange, val.Constant, val.Constant);
StateRange nextRange = ranges[body[i + 1]];
nextRange.UnionWith(nodeRange, int.MinValue, val.Constant - 1);
nextRange.UnionWith(nodeRange, val.Constant + 1, int.MaxValue);
break;
}
case ILCode.Nop: case ILCode.Nop:
ranges[block.Body[i + 1]].UnionWith(nodeRange); ranges[body[i + 1]].UnionWith(nodeRange);
break; break;
case ILCode.Ret: case ILCode.Ret:
break; break;
case ILCode.Stloc:
{
SymbolicValue val = Eval(expr.Arguments[0]);
if (val.Type == SymbolicValueType.State && val.Constant == 0 && rangeAnalysisStateVariable == null)
rangeAnalysisStateVariable = (ILVariable)expr.Operand;
else
throw new YieldAnalysisFailedException();
goto case ILCode.Nop;
}
case ILCode.Call: case ILCode.Call:
// 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
MethodDefinition mdef = expr.Operand as MethodDefinition; if (forDispose) {
if (mdef == null || finallyMethodToStateInterval.ContainsKey(mdef)) MethodDefinition mdef = expr.Operand as MethodDefinition;
if (mdef == null || finallyMethodToStateInterval.ContainsKey(mdef))
throw new YieldAnalysisFailedException();
finallyMethodToStateInterval.Add(mdef, nodeRange.ToEnclosingInterval());
} else {
throw new YieldAnalysisFailedException(); throw new YieldAnalysisFailedException();
finallyMethodToStateInterval.Add(mdef, nodeRange.ToInterval()); }
break; break;
default: default:
throw new YieldAnalysisFailedException(); if (forDispose)
throw new YieldAnalysisFailedException();
else
return i;
} }
} }
return bodyLength;
} }
enum SymbolicValueType enum SymbolicValueType
{ {
/// <summary>
/// int: Constant (result of ldc.i4)
/// </summary>
IntegerConstant, IntegerConstant,
/// <summary>
/// int: State + Constant
/// </summary>
State, State,
This /// <summary>
/// This pointer (result of ldarg.0)
/// </summary>
This,
/// <summary>
/// bool: State == Constant
/// </summary>
StateEquals
} }
struct SymbolicValue struct SymbolicValue
@ -473,11 +553,12 @@ namespace ICSharpCode.Decompiler.ILAst
SymbolicValue Eval(ILExpression expr) SymbolicValue Eval(ILExpression expr)
{ {
SymbolicValue left, right;
switch (expr.Code) { switch (expr.Code) {
case ILCode.Sub: case ILCode.Sub:
SymbolicValue left = Eval(expr.Arguments[0]); left = Eval(expr.Arguments[0]);
SymbolicValue right = Eval(expr.Arguments[1]); right = Eval(expr.Arguments[1]);
if (left.Type != SymbolicValueType.State && right.Type != SymbolicValueType.IntegerConstant) if (left.Type != SymbolicValueType.State && left.Type != SymbolicValueType.IntegerConstant)
throw new YieldAnalysisFailedException(); throw new YieldAnalysisFailedException();
if (right.Type != SymbolicValueType.IntegerConstant) if (right.Type != SymbolicValueType.IntegerConstant)
throw new YieldAnalysisFailedException(); throw new YieldAnalysisFailedException();
@ -488,6 +569,11 @@ namespace ICSharpCode.Decompiler.ILAst
if (expr.Operand != stateField) if (expr.Operand != stateField)
throw new YieldAnalysisFailedException(); throw new YieldAnalysisFailedException();
return new SymbolicValue(SymbolicValueType.State); return new SymbolicValue(SymbolicValueType.State);
case ILCode.Ldloc:
if (expr.Operand == rangeAnalysisStateVariable)
return new SymbolicValue(SymbolicValueType.State);
else
throw new YieldAnalysisFailedException();
case ILCode.Ldarg: case ILCode.Ldarg:
if (((ParameterDefinition)expr.Operand).Index < 0) if (((ParameterDefinition)expr.Operand).Index < 0)
return new SymbolicValue(SymbolicValueType.This); return new SymbolicValue(SymbolicValueType.This);
@ -495,14 +581,21 @@ namespace ICSharpCode.Decompiler.ILAst
throw new YieldAnalysisFailedException(); throw new YieldAnalysisFailedException();
case ILCode.Ldc_I4: case ILCode.Ldc_I4:
return new SymbolicValue(SymbolicValueType.IntegerConstant, (int)expr.Operand); return new SymbolicValue(SymbolicValueType.IntegerConstant, (int)expr.Operand);
case ILCode.Ceq:
left = Eval(expr.Arguments[0]);
right = Eval(expr.Arguments[1]);
if (left.Type != SymbolicValueType.State || right.Type != SymbolicValueType.IntegerConstant)
throw new YieldAnalysisFailedException();
// bool: (state + left.Constant == right.Constant)
// bool: (state == right.Constant - left.Constant)
return new SymbolicValue(SymbolicValueType.StateEquals, unchecked ( right.Constant - left.Constant ));
default: default:
throw new YieldAnalysisFailedException(); throw new YieldAnalysisFailedException();
} }
} }
#endregion #endregion
#endregion
#region Analysis and Transformation of MoveNext() #region Analysis of MoveNext()
ILVariable returnVariable; ILVariable returnVariable;
ILLabel returnLabel; ILLabel returnLabel;
ILLabel returnFalseLabel; ILLabel returnFalseLabel;
@ -547,7 +640,16 @@ namespace ICSharpCode.Decompiler.ILAst
if (tryFaultBlock.CatchBlocks.Count != 0 || tryFaultBlock.FinallyBlock != null || tryFaultBlock.FaultBlock == null) if (tryFaultBlock.CatchBlocks.Count != 0 || tryFaultBlock.FinallyBlock != null || tryFaultBlock.FaultBlock == null)
throw new YieldAnalysisFailedException(); throw new YieldAnalysisFailedException();
VerifyFaultBlock(tryFaultBlock.FaultBlock); ILBlock faultBlock = tryFaultBlock.FaultBlock;
// Ensure the fault block contains the call to Dispose().
if (!(faultBlock.Body.Count == 2 || faultBlock.Body.Count == 3))
throw new YieldAnalysisFailedException();
if (!new ILExpression(ILCode.Call, disposeMethod, LoadFromArgument.This).Match(faultBlock.Body[0]))
throw new YieldAnalysisFailedException();
if (faultBlock.Body.Count == 3 && !faultBlock.Body[1].Match(ILCode.Nop))
throw new YieldAnalysisFailedException();
if (!faultBlock.Body[faultBlock.Body.Count - 1].Match(ILCode.Endfinally))
throw new YieldAnalysisFailedException();
body = tryFaultBlock.TryBlock.Body; body = tryFaultBlock.TryBlock.Body;
body.RemoveAll(n => n.Match(ILCode.Nop)); // remove nops body.RemoveAll(n => n.Match(ILCode.Nop)); // remove nops
@ -578,81 +680,27 @@ namespace ICSharpCode.Decompiler.ILAst
bodyLength -= 2; // don't conside the 'ret(false)' part of the body bodyLength -= 2; // don't conside the 'ret(false)' part of the body
} }
returnFalseLabel = body.ElementAtOrDefault(--bodyLength) as ILLabel; returnFalseLabel = body.ElementAtOrDefault(--bodyLength) as ILLabel;
if (returnFalseLabel == null || bodyLength < 2) if (returnFalseLabel == null || bodyLength == 0)
throw new YieldAnalysisFailedException(); throw new YieldAnalysisFailedException();
// Verify that the first instruction is a switch on this.state, and that the 2nd instruction is br InitStateRanges(body[0]);
if (!new ILExpression(ILCode.Switch, ILExpression.AnyOperand, new ILExpression(ILCode.Ldfld, stateField, LoadFromArgument.This)).Match(body[0])) int pos = AssignStateRanges(body, bodyLength, forDispose: false);
throw new YieldAnalysisFailedException(); while (pos > 0 && body[pos - 1] is ILLabel)
pos--;
if (!body[1].Match(ILCode.Br))
throw new YieldAnalysisFailedException();
SimplifySwitch(body, ref bodyLength); List<KeyValuePair<ILLabel, StateRange>> labels = new List<KeyValuePair<ILLabel, StateRange>>();
for (int i = pos; i < bodyLength; i++) {
// verify that the br (if no state is matched) leads to 'return false'
if (((ILExpression)body[1]).Operand != returnFalseLabel)
throw new YieldAnalysisFailedException();
ConvertBody(body, bodyLength);
}
/// <summary>
/// Ensure the fault block contains the call to Dispose().
/// </summary>
void VerifyFaultBlock(ILBlock faultBlock)
{
ILExpression call;
if (!(faultBlock.Body.Count == 2 || faultBlock.Body.Count == 3))
throw new YieldAnalysisFailedException();
if (!new ILExpression(ILCode.Call, disposeMethod, LoadFromArgument.This).Match(faultBlock.Body[0]))
throw new YieldAnalysisFailedException();
if (faultBlock.Body.Count == 3 && !faultBlock.Body[1].Match(ILCode.Nop))
throw new YieldAnalysisFailedException();
if (!faultBlock.Body[faultBlock.Body.Count - 1].Match(ILCode.Endfinally))
throw new YieldAnalysisFailedException();
}
/// <summary>
/// Simplifies the switch statement at body[0], and the branch at body[1].
/// </summary>
void SimplifySwitch(List<ILNode> body, ref int bodyLength)
{
HashSet<ILLabel> regularLabels = new HashSet<ILLabel>();
Dictionary<ILLabel, ILLabel> simplications = new Dictionary<ILLabel, ILLabel>();
for (int i = 2; i < bodyLength; i++) {
ILExpression expr = body[i] as ILExpression;
if (expr != null && expr.Operand is ILLabel)
regularLabels.Add((ILLabel)expr.Operand);
}
for (int i = 2; i + 1 < bodyLength; i += 2) {
ILLabel label = body[i] as ILLabel; ILLabel label = body[i] as ILLabel;
if (label == null || regularLabels.Contains(label)) if (label != null) {
break; labels.Add(new KeyValuePair<ILLabel, StateRange>(label, ranges[label]));
ILExpression expr = body[i + 1] as ILExpression;
if (expr != null && expr.Code == ILCode.Br) {
simplications.Add(label, (ILLabel)expr.Operand);
} else {
break;
} }
} }
ILExpression switchExpr = (ILExpression)body[0];
ILExpression brExpr = (ILExpression)body[1]; ConvertBody(body, pos, bodyLength, labels);
Debug.Assert(switchExpr.Code == ILCode.Switch);
Debug.Assert(brExpr.Code == ILCode.Br);
ILLabel targetLabel;
if (simplications.TryGetValue((ILLabel)brExpr.Operand, out targetLabel))
brExpr.Operand = targetLabel;
ILLabel[] labels = (ILLabel[])switchExpr.Operand;
for (int i = 0; i < labels.Length; i++) {
if (simplications.TryGetValue(labels[i], out targetLabel))
labels[i] = targetLabel;
}
// remove the labels that aren't used anymore
body.RemoveRange(2, simplications.Count * 2);
bodyLength -= simplications.Count * 2;
} }
#endregion
#region ConvertBody
struct SetState struct SetState
{ {
public readonly int NewBodyPos; public readonly int NewBodyPos;
@ -665,15 +713,14 @@ namespace ICSharpCode.Decompiler.ILAst
} }
} }
void ConvertBody(List<ILNode> body, int bodyLength) void ConvertBody(List<ILNode> body, int startPos, int bodyLength, List<KeyValuePair<ILLabel, StateRange>> labels)
{ {
newBody = new List<ILNode>(); newBody = new List<ILNode>();
ILLabel[] switchLabels = (ILLabel[])((ILExpression)body[0]).Operand; newBody.Add(MakeGoTo(labels, 0));
newBody.Add(MakeGoTo(switchLabels[0]));
List<SetState> stateChanges = new List<SetState>(); List<SetState> stateChanges = new List<SetState>();
int currentState = -1; int currentState = -1;
// Copy all instructions from the old body to newBody. // Copy all instructions from the old body to newBody.
for (int pos = 2; pos < bodyLength; pos++) { for (int pos = startPos; pos < bodyLength; pos++) {
ILExpression expr = body[pos] as ILExpression; ILExpression expr = body[pos] as ILExpression;
if (expr != null && expr.Code == ILCode.Stfld && LoadFromArgument.This.Match(expr.Arguments[0])) { if (expr != null && expr.Code == ILCode.Stfld && LoadFromArgument.This.Match(expr.Arguments[0])) {
// Handle stores to 'state' or 'current' // Handle stores to 'state' or 'current'
@ -696,10 +743,7 @@ namespace ICSharpCode.Decompiler.ILAst
if (val == 0) { if (val == 0) {
newBody.Add(MakeGoTo(returnFalseLabel)); newBody.Add(MakeGoTo(returnFalseLabel));
} else if (val == 1) { } else if (val == 1) {
if (currentState >= 0 && currentState < switchLabels.Length) newBody.Add(MakeGoTo(labels, currentState));
newBody.Add(MakeGoTo(switchLabels[currentState]));
else
newBody.Add(MakeGoTo(returnFalseLabel));
} else { } else {
throw new YieldAnalysisFailedException(); throw new YieldAnalysisFailedException();
} }
@ -711,10 +755,7 @@ namespace ICSharpCode.Decompiler.ILAst
if (val == 0) { if (val == 0) {
newBody.Add(MakeGoTo(returnFalseLabel)); newBody.Add(MakeGoTo(returnFalseLabel));
} else if (val == 1) { } else if (val == 1) {
if (currentState >= 0 && currentState < switchLabels.Length) newBody.Add(MakeGoTo(labels, currentState));
newBody.Add(MakeGoTo(switchLabels[currentState]));
else
newBody.Add(MakeGoTo(returnFalseLabel));
} else { } else {
throw new YieldAnalysisFailedException(); throw new YieldAnalysisFailedException();
} }
@ -766,6 +807,15 @@ namespace ICSharpCode.Decompiler.ILAst
return new ILExpression(ILCode.Br, targetLabel); return new ILExpression(ILCode.Br, targetLabel);
} }
ILExpression MakeGoTo(List<KeyValuePair<ILLabel, StateRange>> labels, int state)
{
foreach (var pair in labels) {
if (pair.Value.Contains(state))
return MakeGoTo(pair.Key);
}
throw new YieldAnalysisFailedException();
}
ILBlock ConvertFinallyBlock(MethodDefinition finallyMethod) ILBlock ConvertFinallyBlock(MethodDefinition finallyMethod)
{ {
ILBlock block = CreateILAst(finallyMethod); ILBlock block = CreateILAst(finallyMethod);

Loading…
Cancel
Save