/// <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();
 }
Esempio n. 2
0
 public MSILCodeBlock(int startIndex, int endIndex, MethodCode mcode) :
     base(startIndex, endIndex, mcode)
 {
 }
Esempio n. 3
0
        /// <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());
        }