/// <summary> /// Constructs a new instance based on a control-flow graph and an assumption on IsCompleted properties. /// </summary> /// <param name="cfg">a control-flow graph</param> /// <param name="assumption">whether the IsCompleted property is assumed to return always true or always false</param> public AsyncMethodCustomInstructionInfo(MethodCode cfg, EAssumption assumption) : base(cfg.Method) { _cfg = cfg; _assumption = assumption; _branchOverrides = new Dictionary <int, int>(); MarkIsCompletedBranches(); }
public MSILCodeBlock(int startIndex, int endIndex, MethodCode mcode) : base(startIndex, endIndex, mcode) { }
/// <summary> /// Given a control-flow graph of an asynchronous method, finds all state entry points. /// This is done using some pattern matching and knowledge on how the compiler translates asynchronous methods. /// Therefore, some special cases might not be resolved correctly, and the code might break down with future compiler revisions. /// </summary> /// <param name="cfg">a control-flow graph</param> /// <returns>all state entry points</returns> public static MSILCodeBlock[] FindStateTargets(MethodCode cfg) { var entry = cfg.BasicBlocks[0]; FieldInfo statefield = null; int stateLocalIndex = -1; int stateValue = int.MinValue; int stateOffset = 0; var mode = EStateLocMode.LocatingStateField; var q = new Queue <Tuple <int, MSILCodeBlock> >(); foreach (var cili in entry.Range) { switch (mode) { case EStateLocMode.LocatingStateField: if (cili.Code == OpCodes.Ldfld) { // this must be the state field statefield = (FieldInfo)cili.Operand; mode = EStateLocMode.LocatingStateLocal; } break; case EStateLocMode.LocatingStateLocal: { int index; if (IsStloc(cili, out index)) { stateLocalIndex = index; } } break; } int value; if (IsLdc_I(cili, out value)) { stateValue = value; } if (cili.Code == OpCodes.Sub) { stateOffset = stateValue; } else if (cili.Code == OpCodes.Beq || cili.Code == OpCodes.Beq_S) { // State -3 exists only in debug builds and skips the whole FSM => irrelevant if (stateValue != -3) { q.Enqueue(Tuple.Create(stateValue, entry.Successors[1])); } q.Enqueue(Tuple.Create(-1, entry.Successors[0])); } else if (cili.Code == OpCodes.Switch) { bool have_1 = false; for (int i = 1; i < entry.Successors.Length; i++) { int state = i - 1 + stateOffset; // State -3 exists only in debug builds and skips the whole FSM => irrelevant // State -2 is completion state => also irrelevant if (state >= -1) { q.Enqueue(Tuple.Create(state, entry.Successors[i])); if (state == -1) { have_1 = true; } } } if (!have_1) { q.Enqueue(Tuple.Create(-1, entry.Successors[0])); } } } var stateDic = new SortedDictionary <int, MSILCodeBlock>(); while (q.Any()) { var tup = q.Dequeue(); int state = tup.Item1; var block = tup.Item2; int value; if (IsLdloc(block.Range.First(), out value) && value == stateLocalIndex) { if (block.Range.Count != 3) { throw new NotSupportedException("Decompilation of await pattern failed: expected 3 instructions in this block."); } if (!IsLdc_I(block.Range[1], out stateValue)) { throw new NotSupportedException("Decompilation of await pattern failed: expected Ldc_I<x> op-code."); } if (block.Range[2].Code != OpCodes.Beq && block.Range[2].Code != OpCodes.Beq_S) { throw new NotSupportedException("Decompilation of await pattern failed: expected Beq/Beq_S op-code."); } q.Enqueue(Tuple.Create(stateValue, block.Successors[1])); q.Enqueue(Tuple.Create(-1, block.Successors[0])); } else { stateDic[state] = block; } } for (int state = -1; state < stateDic.Count - 1; state++) { if (!stateDic.ContainsKey(state)) { throw new NotSupportedException("Decompilation of await pattern failed: expected consecutive states from -1 to " + (stateDic.Count - 1)); } } return(stateDic.Values.ToArray()); }