void AnalyzeMoveNext()
        {
            MethodDefinition moveNextMethod = enumeratorType.Methods.FirstOrDefault(m => m.Name == "MoveNext");
            ILBlock          ilMethod       = CreateILAst(moveNextMethod);

            if (ilMethod.Body.Count == 0)
            {
                throw new YieldAnalysisFailedException();
            }
            ILExpression lastReturnArg;

            if (!ilMethod.Body.Last().Match(ILCode.Ret, out lastReturnArg))
            {
                throw new YieldAnalysisFailedException();
            }

            // There are two possibilities:
            if (lastReturnArg.Code == ILCode.Ldloc)
            {
                // a) the compiler uses a variable for returns (in debug builds, or when there are try-finally blocks)
                returnVariable = (ILVariable)lastReturnArg.Operand;
                returnLabel    = ilMethod.Body.ElementAtOrDefault(ilMethod.Body.Count - 2) as ILLabel;
                if (returnLabel == null)
                {
                    throw new YieldAnalysisFailedException();
                }
            }
            else
            {
                // b) the compiler directly returns constants
                returnVariable = null;
                returnLabel    = null;
                // In this case, the last return must return false.
                if (lastReturnArg.Code != ILCode.Ldc_I4 || (int)lastReturnArg.Operand != 0)
                {
                    throw new YieldAnalysisFailedException();
                }
            }

            ILTryCatchBlock tryFaultBlock = ilMethod.Body[0] as ILTryCatchBlock;
            List <ILNode>   body;
            int             bodyLength;

            if (tryFaultBlock != null)
            {
                // there are try-finally blocks
                if (returnVariable == null)                 // in this case, we must use a return variable
                {
                    throw new YieldAnalysisFailedException();
                }
                // must be a try-fault block:
                if (tryFaultBlock.CatchBlocks.Count != 0 || tryFaultBlock.FinallyBlock != null || tryFaultBlock.FaultBlock == null)
                {
                    throw new YieldAnalysisFailedException();
                }

                ILBlock faultBlock = tryFaultBlock.FaultBlock;
                // Ensure the fault block contains the call to Dispose().
                if (faultBlock.Body.Count != 2)
                {
                    throw new YieldAnalysisFailedException();
                }
                MethodReference disposeMethodRef;
                ILExpression    disposeArg;
                if (!faultBlock.Body[0].Match(ILCode.Call, out disposeMethodRef, out disposeArg))
                {
                    throw new YieldAnalysisFailedException();
                }
                if (GetMethodDefinition(disposeMethodRef) != disposeMethod || !disposeArg.MatchThis())
                {
                    throw new YieldAnalysisFailedException();
                }
                if (!faultBlock.Body[1].Match(ILCode.Endfinally))
                {
                    throw new YieldAnalysisFailedException();
                }

                body       = tryFaultBlock.TryBlock.Body;
                bodyLength = body.Count;
            }
            else
            {
                // no try-finally blocks
                body = ilMethod.Body;
                if (returnVariable == null)
                {
                    bodyLength = body.Count - 1;                     // all except for the return statement
                }
                else
                {
                    bodyLength = body.Count - 2;                     // all except for the return label and statement
                }
            }

            // Now verify that the last instruction in the body is 'ret(false)'
            if (returnVariable != null)
            {
                // If we don't have a return variable, we already verified that above.
                // If we do have one, check for 'stloc(returnVariable, ldc.i4(0))'

                // Maybe might be a jump to the return label after the stloc:
                ILExpression leave = body.ElementAtOrDefault(bodyLength - 1) as ILExpression;
                if (leave != null && (leave.Code == ILCode.Br || leave.Code == ILCode.Leave) && leave.Operand == returnLabel)
                {
                    bodyLength--;
                }
                ILExpression store0 = body.ElementAtOrDefault(bodyLength - 1) as ILExpression;
                if (store0 == null || store0.Code != ILCode.Stloc || store0.Operand != returnVariable)
                {
                    throw new YieldAnalysisFailedException();
                }
                if (store0.Arguments[0].Code != ILCode.Ldc_I4 || (int)store0.Arguments[0].Operand != 0)
                {
                    throw new YieldAnalysisFailedException();
                }

                bodyLength--;                 // don't conside the stloc instruction to be part of the body
            }
            // verify that the last element in the body is a label pointing to the 'ret(false)'
            returnFalseLabel = body.ElementAtOrDefault(bodyLength - 1) as ILLabel;
            if (returnFalseLabel == null)
            {
                throw new YieldAnalysisFailedException();
            }

            InitStateRanges(body[0]);
            int pos = AssignStateRanges(body, bodyLength, forDispose: false);

            if (pos > 0 && body[pos - 1] is ILLabel)
            {
                pos--;
            }
            else
            {
                // ensure that the first element at body[pos] is a label:
                ILLabel newLabel = new ILLabel();
                newLabel.Name    = "YieldReturnEntryPoint";
                ranges[newLabel] = ranges[body[pos]];                 // give the label the range of the instruction at body[pos]

                body.Insert(pos, newLabel);
                bodyLength++;
            }

            List <KeyValuePair <ILLabel, StateRange> > labels = new List <KeyValuePair <ILLabel, StateRange> >();

            for (int i = pos; i < bodyLength; i++)
            {
                ILLabel label = body[i] as ILLabel;
                if (label != null)
                {
                    labels.Add(new KeyValuePair <ILLabel, StateRange>(label, ranges[label]));
                }
            }

            ConvertBody(body, pos, bodyLength, labels);
        }
        void ConvertBody(List <ILNode> body, int startPos, int bodyLength, List <KeyValuePair <ILLabel, StateRange> > labels)
        {
            newBody = new List <ILNode>();
            newBody.Add(MakeGoTo(labels, 0));
            List <SetState> stateChanges = new List <SetState>();
            int             currentState = -1;

            // Copy all instructions from the old body to newBody.
            for (int pos = startPos; pos < bodyLength; pos++)
            {
                ILExpression expr = body[pos] as ILExpression;
                if (expr != null && expr.Code == ILCode.Stfld && expr.Arguments[0].MatchThis())
                {
                    // Handle stores to 'state' or 'current'
                    if (GetFieldDefinition(expr.Operand as FieldReference) == stateField)
                    {
                        if (expr.Arguments[1].Code != ILCode.Ldc_I4)
                        {
                            throw new YieldAnalysisFailedException();
                        }
                        currentState = (int)expr.Arguments[1].Operand;
                        stateChanges.Add(new SetState(newBody.Count, currentState));
                    }
                    else if (GetFieldDefinition(expr.Operand as FieldReference) == currentField)
                    {
                        newBody.Add(new ILExpression(ILCode.YieldReturn, null, expr.Arguments[1]));
                    }
                    else
                    {
                        newBody.Add(body[pos]);
                    }
                }
                else if (returnVariable != null && expr != null && expr.Code == ILCode.Stloc && expr.Operand == returnVariable)
                {
                    // handle store+branch to the returnVariable
                    ILExpression br = body.ElementAtOrDefault(++pos) as ILExpression;
                    if (br == null || !(br.Code == ILCode.Br || br.Code == ILCode.Leave) || br.Operand != returnLabel || expr.Arguments[0].Code != ILCode.Ldc_I4)
                    {
                        throw new YieldAnalysisFailedException();
                    }
                    int val = (int)expr.Arguments[0].Operand;
                    if (val == 0)
                    {
                        newBody.Add(MakeGoTo(returnFalseLabel));
                    }
                    else if (val == 1)
                    {
                        newBody.Add(MakeGoTo(labels, currentState));
                    }
                    else
                    {
                        throw new YieldAnalysisFailedException();
                    }
                }
                else if (expr != null && expr.Code == ILCode.Ret)
                {
                    if (expr.Arguments.Count != 1 || expr.Arguments[0].Code != ILCode.Ldc_I4)
                    {
                        throw new YieldAnalysisFailedException();
                    }
                    // handle direct return (e.g. in release builds)
                    int val = (int)expr.Arguments[0].Operand;
                    if (val == 0)
                    {
                        newBody.Add(MakeGoTo(returnFalseLabel));
                    }
                    else if (val == 1)
                    {
                        newBody.Add(MakeGoTo(labels, currentState));
                    }
                    else
                    {
                        throw new YieldAnalysisFailedException();
                    }
                }
                else if (expr != null && expr.Code == ILCode.Call && expr.Arguments.Count == 1 && expr.Arguments[0].MatchThis())
                {
                    MethodDefinition method = GetMethodDefinition(expr.Operand as MethodReference);
                    if (method == null)
                    {
                        throw new YieldAnalysisFailedException();
                    }
                    Interval interval;
                    if (method == disposeMethod)
                    {
                        // Explicit call to dispose is used for "yield break;" within the method.
                        ILExpression br = body.ElementAtOrDefault(++pos) as ILExpression;
                        if (br == null || !(br.Code == ILCode.Br || br.Code == ILCode.Leave) || br.Operand != returnFalseLabel)
                        {
                            throw new YieldAnalysisFailedException();
                        }
                        newBody.Add(MakeGoTo(returnFalseLabel));
                    }
                    else if (finallyMethodToStateInterval.TryGetValue(method, out interval))
                    {
                        // Call to Finally-method
                        int index = stateChanges.FindIndex(ss => ss.NewState >= interval.Start && ss.NewState <= interval.End);
                        if (index < 0)
                        {
                            throw new YieldAnalysisFailedException();
                        }

                        ILLabel label = new ILLabel();
                        label.Name = "JumpOutOfTryFinally" + interval.Start + "_" + interval.End;
                        newBody.Add(new ILExpression(ILCode.Leave, label));

                        SetState stateChange = stateChanges[index];
                        // Move all instructions from stateChange.Pos to newBody.Count into a try-block
                        stateChanges.RemoveRange(index, stateChanges.Count - index);                         // remove all state changes up to the one we found
                        ILTryCatchBlock tryFinally = new ILTryCatchBlock();
                        tryFinally.TryBlock = new ILBlock(newBody.GetRange(stateChange.NewBodyPos, newBody.Count - stateChange.NewBodyPos));
                        newBody.RemoveRange(stateChange.NewBodyPos, newBody.Count - stateChange.NewBodyPos);                         // remove all nodes that we just moved into the try block
                        tryFinally.CatchBlocks  = new List <ILTryCatchBlock.CatchBlock>();
                        tryFinally.FinallyBlock = ConvertFinallyBlock(method);
                        newBody.Add(tryFinally);
                        newBody.Add(label);
                    }
                }
                else
                {
                    newBody.Add(body[pos]);
                }
            }
            newBody.Add(new ILExpression(ILCode.YieldBreak, null));
        }
        int AssignStateRanges(List <ILNode> body, int bodyLength, bool forDispose)
        {
            if (bodyLength == 0)
            {
                return(0);
            }
            for (int i = 0; i < bodyLength; i++)
            {
                StateRange nodeRange = ranges[body[i]];
                nodeRange.Simplify();

                ILLabel label = body[i] as ILLabel;
                if (label != null)
                {
                    ranges[body[i + 1]].UnionWith(nodeRange);
                    continue;
                }

                ILTryCatchBlock tryFinally = body[i] as ILTryCatchBlock;
                if (tryFinally != null)
                {
                    if (!forDispose || tryFinally.CatchBlocks.Count != 0 || tryFinally.FaultBlock != null || tryFinally.FinallyBlock == null)
                    {
                        throw new YieldAnalysisFailedException();
                    }
                    ranges[tryFinally.TryBlock].UnionWith(nodeRange);
                    if (tryFinally.TryBlock.Body.Count != 0)
                    {
                        ranges[tryFinally.TryBlock.Body[0]].UnionWith(nodeRange);
                        AssignStateRanges(tryFinally.TryBlock.Body, tryFinally.TryBlock.Body.Count, forDispose);
                    }
                    continue;
                }

                ILExpression expr = body[i] as ILExpression;
                if (expr == null)
                {
                    throw new YieldAnalysisFailedException();
                }
                switch (expr.Code)
                {
                case ILCode.Switch:
                {
                    SymbolicValue val = Eval(expr.Arguments[0]);
                    if (val.Type != SymbolicValueType.State)
                    {
                        throw new YieldAnalysisFailedException();
                    }
                    ILLabel[] targetLabels = (ILLabel[])expr.Operand;
                    for (int j = 0; j < targetLabels.Length; j++)
                    {
                        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;
                }

                case ILCode.Br:
                case ILCode.Leave:
                    ranges[(ILLabel)expr.Operand].UnionWith(nodeRange);
                    break;

                case ILCode.Brtrue:
                {
                    SymbolicValue val = Eval(expr.Arguments[0]);
                    if (val.Type == SymbolicValueType.StateEquals)
                    {
                        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);
                    }
                    else if (val.Type == SymbolicValueType.StateInEquals)
                    {
                        ranges[body[i + 1]].UnionWith(nodeRange, val.Constant, val.Constant);
                        StateRange targetRange = ranges[(ILLabel)expr.Operand];
                        targetRange.UnionWith(nodeRange, int.MinValue, val.Constant - 1);
                        targetRange.UnionWith(nodeRange, val.Constant + 1, int.MaxValue);
                    }
                    else
                    {
                        throw new YieldAnalysisFailedException();
                    }
                    break;
                }

                case ILCode.Nop:
                    ranges[body[i + 1]].UnionWith(nodeRange);
                    break;

                case ILCode.Ret:
                    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:
                    // in some cases (e.g. foreach over array) the C# compiler produces a finally method outside of try-finally blocks
                    if (forDispose)
                    {
                        MethodDefinition mdef = GetMethodDefinition(expr.Operand as MethodReference);
                        if (mdef == null || finallyMethodToStateInterval.ContainsKey(mdef))
                        {
                            throw new YieldAnalysisFailedException();
                        }
                        finallyMethodToStateInterval.Add(mdef, nodeRange.ToEnclosingInterval());
                    }
                    else
                    {
                        throw new YieldAnalysisFailedException();
                    }
                    break;

                default:
                    if (forDispose)
                    {
                        throw new YieldAnalysisFailedException();
                    }
                    else
                    {
                        return(i);
                    }
                }
            }
            return(bodyLength);
        }
Ejemplo n.º 4
0
        void AnalyzeMoveNext()
        {
            MethodDefinition moveNextMethod = enumeratorType.Methods.FirstOrDefault(m => m.Name == "MoveNext");
            ILBlock          ilMethod       = CreateILAst(moveNextMethod);

            if (ilMethod.Body.Count == 0)
            {
                throw new SymbolicAnalysisFailedException();
            }
            ILExpression lastReturnArg;

            if (!ilMethod.Body.Last().Match(ILCode.Ret, out lastReturnArg))
            {
                throw new SymbolicAnalysisFailedException();
            }

            // There are two possibilities:
            if (lastReturnArg.Code == ILCode.Ldloc)
            {
                // a) the compiler uses a variable for returns (in debug builds, or when there are try-finally blocks)
                returnVariable = (ILVariable)lastReturnArg.Operand;
                returnLabel    = ilMethod.Body.ElementAtOrDefault(ilMethod.Body.Count - 2) as ILLabel;
                if (returnLabel == null)
                {
                    throw new SymbolicAnalysisFailedException();
                }
            }
            else
            {
                // b) the compiler directly returns constants
                returnVariable = null;
                returnLabel    = null;
                // In this case, the last return must return false.
                if (lastReturnArg.Code != ILCode.Ldc_I4 || (int)lastReturnArg.Operand != 0)
                {
                    throw new SymbolicAnalysisFailedException();
                }
            }

            ILTryCatchBlock tryFaultBlock = ilMethod.Body[0] as ILTryCatchBlock;
            List <ILNode>   body;
            int             bodyLength;

            if (tryFaultBlock != null)
            {
                // there are try-finally blocks
                if (returnVariable == null)                 // in this case, we must use a return variable
                {
                    throw new SymbolicAnalysisFailedException();
                }
                // must be a try-fault block:
                if (tryFaultBlock.CatchBlocks.Count != 0 || tryFaultBlock.FinallyBlock != null || tryFaultBlock.FaultBlock == null)
                {
                    throw new SymbolicAnalysisFailedException();
                }

                ILBlock faultBlock = tryFaultBlock.FaultBlock;
                // Ensure the fault block contains the call to Dispose().
                if (faultBlock.Body.Count != 2)
                {
                    throw new SymbolicAnalysisFailedException();
                }
                MethodReference disposeMethodRef;
                ILExpression    disposeArg;
                if (!faultBlock.Body[0].Match(ILCode.Call, out disposeMethodRef, out disposeArg))
                {
                    throw new SymbolicAnalysisFailedException();
                }
                if (GetMethodDefinition(disposeMethodRef) != disposeMethod || !disposeArg.MatchThis())
                {
                    throw new SymbolicAnalysisFailedException();
                }
                if (!faultBlock.Body[1].Match(ILCode.Endfinally))
                {
                    throw new SymbolicAnalysisFailedException();
                }

                body       = tryFaultBlock.TryBlock.Body;
                bodyLength = body.Count;
            }
            else
            {
                // no try-finally blocks
                body = ilMethod.Body;
                if (returnVariable == null)
                {
                    bodyLength = body.Count - 1;                     // all except for the return statement
                }
                else
                {
                    bodyLength = body.Count - 2;                     // all except for the return label and statement
                }
            }

            // Now verify that the last instruction in the body is 'ret(false)'
            if (returnVariable != null)
            {
                // If we don't have a return variable, we already verified that above.
                // If we do have one, check for 'stloc(returnVariable, ldc.i4(0))'

                // Maybe might be a jump to the return label after the stloc:
                ILExpression leave = body.ElementAtOrDefault(bodyLength - 1) as ILExpression;
                if (leave != null && (leave.Code == ILCode.Br || leave.Code == ILCode.Leave) && leave.Operand == returnLabel)
                {
                    bodyLength--;
                }
                ILExpression store0 = body.ElementAtOrDefault(bodyLength - 1) as ILExpression;
                if (store0 == null || store0.Code != ILCode.Stloc || store0.Operand != returnVariable)
                {
                    throw new SymbolicAnalysisFailedException();
                }
                if (store0.Arguments[0].Code != ILCode.Ldc_I4 || (int)store0.Arguments[0].Operand != 0)
                {
                    throw new SymbolicAnalysisFailedException();
                }

                bodyLength--;                 // don't conside the stloc instruction to be part of the body
            }
            // The last element in the body usually is a label pointing to the 'ret(false)'
            returnFalseLabel = body.ElementAtOrDefault(bodyLength - 1) as ILLabel;
            // Note: in Roslyn-compiled code, returnFalseLabel may be null.

            var rangeAnalysis = new StateRangeAnalysis(body[0], StateRangeAnalysisMode.IteratorMoveNext, stateField);
            int pos           = rangeAnalysis.AssignStateRanges(body, bodyLength);

            rangeAnalysis.EnsureLabelAtPos(body, ref pos, ref bodyLength);

            var labels = rangeAnalysis.CreateLabelRangeMapping(body, pos, bodyLength);

            ConvertBody(body, pos, bodyLength, labels);
        }
Ejemplo n.º 5
0
        List <ILNode> ConvertToAst(List <ByteCode> body, HashSet <ExceptionHandler> ehs)
        {
            List <ILNode> ast = new List <ILNode>();

            while (ehs.Any())
            {
                ILTryCatchBlock tryCatchBlock = new ILTryCatchBlock();

                // Find the first and widest scope
                uint tryStart = ehs.Min(eh => eh.TryStart.GetOffset());
                uint tryEnd   = ehs.Where(eh => eh.TryStart.GetOffset() == tryStart).Max(eh => eh.TryEnd.GetOffset());
                var  handlers = ehs.Where(eh => eh.TryStart.GetOffset() == tryStart && eh.TryEnd.GetOffset() == tryEnd).ToList();

                // Remember that any part of the body migt have been removed due to unreachability

                // Cut all instructions up to the try block
                {
                    int tryStartIdx = 0;
                    while (tryStartIdx < body.Count && body[tryStartIdx].Offset < tryStart)
                    {
                        tryStartIdx++;
                    }
                    ast.AddRange(ConvertToAst(body.CutRange(0, tryStartIdx)));
                }

                // Cut the try block
                {
                    HashSet <ExceptionHandler> nestedEHs = new HashSet <ExceptionHandler>(ehs.Where(eh => (tryStart <= eh.TryStart.GetOffset() && eh.TryEnd.GetOffset() < tryEnd) || (tryStart < eh.TryStart.GetOffset() && eh.TryEnd.GetOffset() <= tryEnd)));
                    ehs.ExceptWith(nestedEHs);
                    int tryEndIdx = 0;
                    while (tryEndIdx < body.Count && body[tryEndIdx].Offset < tryEnd)
                    {
                        tryEndIdx++;
                    }
                    tryCatchBlock.TryBlock = new ILBlock(ConvertToAst(body.CutRange(0, tryEndIdx), nestedEHs));
                }

                // Cut all handlers
                tryCatchBlock.CatchBlocks = new List <ILTryCatchBlock.CatchBlock>();
                foreach (ExceptionHandler eh in handlers)
                {
                    uint handlerEndOffset = eh.HandlerEnd == null ? (uint)methodDef.Body.GetCodeSize() : eh.HandlerEnd.Offset;
                    int  startIdx         = 0;
                    while (startIdx < body.Count && body[startIdx].Offset < eh.HandlerStart.GetOffset())
                    {
                        startIdx++;
                    }
                    int endIdx = 0;
                    while (endIdx < body.Count && body[endIdx].Offset < handlerEndOffset)
                    {
                        endIdx++;
                    }
                    HashSet <ExceptionHandler> nestedEHs = new HashSet <ExceptionHandler>(ehs.Where(e => (eh.HandlerStart.GetOffset() <= e.TryStart.GetOffset() && e.TryEnd.GetOffset() < handlerEndOffset) || (eh.HandlerStart.GetOffset() < e.TryStart.GetOffset() && e.TryEnd.GetOffset() <= handlerEndOffset)));
                    ehs.ExceptWith(nestedEHs);
                    List <ILNode> handlerAst = ConvertToAst(body.CutRange(startIdx, endIdx - startIdx), nestedEHs);
                    if (eh.HandlerType == ExceptionHandlerType.Catch)
                    {
                        ILTryCatchBlock.CatchBlock catchBlock = new ILTryCatchBlock.CatchBlock(handlerAst)
                        {
                            ExceptionType = eh.CatchType.ToTypeSig(),
                        };
                        // Handle the automatically pushed exception on the stack
                        ByteCode ldexception = ldexceptions[eh];
                        ConvertExceptionVariable(eh, catchBlock, ldexception);
                        tryCatchBlock.CatchBlocks.Add(catchBlock);
                    }
                    else if (eh.HandlerType == ExceptionHandlerType.Finally)
                    {
                        tryCatchBlock.FinallyBlock = new ILBlock(handlerAst);
                    }
                    else if (eh.HandlerType == ExceptionHandlerType.Fault)
                    {
                        tryCatchBlock.FaultBlock = new ILBlock(handlerAst);
                    }
                    else if (useNewFilterCode && eh.HandlerType == ExceptionHandlerType.Filter)
                    {
                        ILTryCatchBlock.CatchBlock catchBlock = new ILTryCatchBlock.CatchBlock(handlerAst)
                        {
                            ExceptionType = eh.CatchType.ToTypeSig(),
                        };
                        // Handle the automatically pushed exception on the stack
                        ByteCode ldexception = ldexceptions[eh];
                        ConvertExceptionVariable(eh, catchBlock, ldexception);

                        // Extract the filter part
                        startIdx = 0;
                        while (startIdx < body.Count && body[startIdx].Offset < eh.FilterStart.GetOffset())
                        {
                            startIdx++;
                        }
                        endIdx = 0;
                        while (endIdx < body.Count && body[endIdx].Offset < eh.HandlerStart.GetOffset())
                        {
                            endIdx++;
                        }
                        nestedEHs = new HashSet <ExceptionHandler>(ehs.Where(e => (eh.FilterStart.GetOffset() <= e.TryStart.GetOffset() && e.TryEnd.GetOffset() < eh.HandlerStart.GetOffset()) || (eh.FilterStart.GetOffset() < e.TryStart.GetOffset() && e.TryEnd.GetOffset() <= eh.HandlerStart.GetOffset())));
                        ehs.ExceptWith(nestedEHs);
                        List <ILNode> filterAst   = ConvertToAst(body.CutRange(startIdx, endIdx - startIdx), nestedEHs);
                        var           filterBlock = new ILTryCatchBlock.FilterILBlock()
                        {
                            Body = filterAst
                        };
                        ByteCode ldfilter = ldfilters[eh];
                        ConvertExceptionVariable(eh, filterBlock, ldfilter);
                        filterBlock.HandlerBlock = catchBlock;
                        filterBlock.StlocILRanges.AddRange(catchBlock.StlocILRanges);

                        tryCatchBlock.FilterBlock = filterBlock;
                    }
                    else
                    {
                    }
                }

                ehs.ExceptWith(handlers);

                ast.Add(tryCatchBlock);
            }

            // Add whatever is left
            ast.AddRange(ConvertToAst(body));

            return(ast);
        }
Ejemplo n.º 6
0
        List <ILNode> ConvertToAst(List <ByteCode> body, HashSet <ExceptionHandler> ehs)
        {
            List <ILNode> ast = new List <ILNode>();

            while (ehs.Any())
            {
                ILTryCatchBlock tryCatchBlock = new ILTryCatchBlock();

                // Find the first and widest scope
                int tryStart = ehs.Min(eh => eh.TryStart.Offset);
                int tryEnd   = ehs.Where(eh => eh.TryStart.Offset == tryStart).Max(eh => eh.TryEnd.Offset);
                var handlers = ehs.Where(eh => eh.TryStart.Offset == tryStart && eh.TryEnd.Offset == tryEnd).ToList();

                // Cut all instructions up to the try block
                {
                    int tryStartIdx;
                    for (tryStartIdx = 0; body[tryStartIdx].Offset != tryStart; tryStartIdx++)
                    {
                        ;
                    }
                    ast.AddRange(ConvertToAst(body.CutRange(0, tryStartIdx)));
                }

                // Cut the try block
                {
                    HashSet <ExceptionHandler> nestedEHs = new HashSet <ExceptionHandler>(ehs.Where(eh => (tryStart <= eh.TryStart.Offset && eh.TryEnd.Offset < tryEnd) || (tryStart < eh.TryStart.Offset && eh.TryEnd.Offset <= tryEnd)));
                    ehs.ExceptWith(nestedEHs);
                    int tryEndIdx;
                    for (tryEndIdx = 0; tryEndIdx < body.Count && body[tryEndIdx].Offset != tryEnd; tryEndIdx++)
                    {
                        ;
                    }
                    tryCatchBlock.TryBlock = new ILBlock(ConvertToAst(body.CutRange(0, tryEndIdx), nestedEHs));
                }

                // Cut all handlers
                tryCatchBlock.CatchBlocks = new List <ILTryCatchBlock.CatchBlock>();
                foreach (ExceptionHandler eh in handlers)
                {
                    int startIndex;
                    for (startIndex = 0; body[startIndex].Offset != eh.HandlerStart.Offset; startIndex++)
                    {
                        ;
                    }
                    int endInclusiveIndex;
                    if (eh.HandlerEnd == null)
                    {
                        endInclusiveIndex = body.Count - 1;
                    }
                    // Note that the end(exclusive) instruction may not necessarly be in our body
                    else
                    {
                        for (endInclusiveIndex = 0; body[endInclusiveIndex].Next.Offset != eh.HandlerEnd.Offset; endInclusiveIndex++)
                        {
                            ;
                        }
                    }
                    int count = 1 + endInclusiveIndex - startIndex;
                    HashSet <ExceptionHandler> nestedEHs = new HashSet <ExceptionHandler>(ehs.Where(e => (eh.HandlerStart.Offset <= e.TryStart.Offset && e.TryEnd.Offset < eh.HandlerEnd.Offset) || (eh.HandlerStart.Offset < e.TryStart.Offset && e.TryEnd.Offset <= eh.HandlerEnd.Offset)));
                    ehs.ExceptWith(nestedEHs);
                    List <ILNode> handlerAst = ConvertToAst(body.CutRange(startIndex, count), nestedEHs);
                    if (eh.HandlerType == ExceptionHandlerType.Catch)
                    {
                        ILTryCatchBlock.CatchBlock catchBlock = new ILTryCatchBlock.CatchBlock()
                        {
                            ExceptionType = eh.CatchType,
                            Body          = handlerAst
                        };
                        // Handle the automatically pushed exception on the stack
                        ByteCode ldexception = ldexceptions[eh];
                        if (ldexception.StoreTo.Count == 0)
                        {
                            throw new Exception("Exception should be consumed by something");
                        }
                        else if (ldexception.StoreTo.Count == 1)
                        {
                            ILExpression first = catchBlock.Body[0] as ILExpression;
                            if (first != null &&
                                first.Code == ILCode.Pop &&
                                first.Arguments[0].Code == ILCode.Ldloc &&
                                first.Arguments[0].Operand == ldexception.StoreTo[0])
                            {
                                // The exception is just poped - optimize it all away;
                                catchBlock.ExceptionVariable = null;
                                catchBlock.Body.RemoveAt(0);
                            }
                            else
                            {
                                catchBlock.ExceptionVariable = ldexception.StoreTo[0];
                            }
                        }
                        else
                        {
                            ILVariable exTemp = new ILVariable()
                            {
                                Name = "ex_" + eh.HandlerStart.Offset.ToString("X2"), IsGenerated = true
                            };
                            catchBlock.ExceptionVariable = exTemp;
                            foreach (ILVariable storeTo in ldexception.StoreTo)
                            {
                                catchBlock.Body.Insert(0, new ILExpression(ILCode.Stloc, storeTo, new ILExpression(ILCode.Ldloc, exTemp)));
                            }
                        }
                        tryCatchBlock.CatchBlocks.Add(catchBlock);
                    }
                    else if (eh.HandlerType == ExceptionHandlerType.Finally)
                    {
                        tryCatchBlock.FinallyBlock = new ILBlock(handlerAst);
                    }
                    else if (eh.HandlerType == ExceptionHandlerType.Fault)
                    {
                        tryCatchBlock.FaultBlock = new ILBlock(handlerAst);
                    }
                    else
                    {
                        // TODO: ExceptionHandlerType.Filter
                    }
                }

                ehs.ExceptWith(handlers);

                ast.Add(tryCatchBlock);
            }

            // Add whatever is left
            ast.AddRange(ConvertToAst(body));

            return(ast);
        }
Ejemplo n.º 7
0
        void AnalyzeMoveNext()
        {
            ILBlock ilMethod = CreateILAst(moveNextMethod);

            int startIndex;

            if (ilMethod.Body.Count == 6)
            {
                startIndex = 0;
            }
            else if (ilMethod.Body.Count == 7)
            {
                // stloc(cachedState, ldfld(valuetype StateMachineStruct::<>1__state, ldloc(this)))
                ILExpression cachedStateInit;
                if (!ilMethod.Body[0].Match(ILCode.Stloc, out cachedStateVar, out cachedStateInit))
                {
                    throw new SymbolicAnalysisFailedException();
                }
                ILExpression   instanceExpr;
                FieldReference loadedField;
                if (!cachedStateInit.Match(ILCode.Ldfld, out loadedField, out instanceExpr) || loadedField.ResolveWithinSameModule() != stateField || !instanceExpr.MatchThis())
                {
                    throw new SymbolicAnalysisFailedException();
                }
                startIndex = 1;
            }
            else
            {
                throw new SymbolicAnalysisFailedException();
            }

            mainTryCatch = ilMethod.Body[startIndex + 0] as ILTryCatchBlock;
            if (mainTryCatch == null || mainTryCatch.CatchBlocks.Count != 1)
            {
                throw new SymbolicAnalysisFailedException();
            }
            if (mainTryCatch.FaultBlock != null || mainTryCatch.FinallyBlock != null)
            {
                throw new SymbolicAnalysisFailedException();
            }

            setResultAndExitLabel = ilMethod.Body[startIndex + 1] as ILLabel;
            if (setResultAndExitLabel == null)
            {
                throw new SymbolicAnalysisFailedException();
            }

            if (!MatchStateAssignment(ilMethod.Body[startIndex + 2], out finalState))
            {
                throw new SymbolicAnalysisFailedException();
            }

            // call(AsyncTaskMethodBuilder`1::SetResult, ldflda(StateMachine::<>t__builder, ldloc(this)), ldloc(<>t__result))
            MethodReference setResultMethod;
            ILExpression    builderExpr;

            if (methodType == AsyncMethodType.TaskOfT)
            {
                if (!ilMethod.Body[startIndex + 3].Match(ILCode.Call, out setResultMethod, out builderExpr, out resultExpr))
                {
                    throw new SymbolicAnalysisFailedException();
                }
            }
            else
            {
                if (!ilMethod.Body[startIndex + 3].Match(ILCode.Call, out setResultMethod, out builderExpr))
                {
                    throw new SymbolicAnalysisFailedException();
                }
            }
            if (!(setResultMethod.Name == "SetResult" && IsBuilderFieldOnThis(builderExpr)))
            {
                throw new SymbolicAnalysisFailedException();
            }

            exitLabel = ilMethod.Body[startIndex + 4] as ILLabel;
            if (exitLabel == null)
            {
                throw new SymbolicAnalysisFailedException();
            }
        }
Ejemplo n.º 8
0
        /// <summary>
        /// Get the first expression to be excecuted if the instruction pointer is at the start of the given node.
        /// Try blocks may not be entered in any way.  If possible, the try block is returned as the node to be executed.
        /// </summary>
        ILNode Enter(ILNode node, HashSet <ILNode> visitedNodes)
        {
            if (node == null)
            {
                throw new ArgumentNullException();
            }

            if (!visitedNodes.Add(node))
            {
                return(null);                 // Infinite loop
            }
            ILLabel label = node as ILLabel;

            if (label != null)
            {
                return(Exit(label, visitedNodes));
            }

            ILExpression expr = node as ILExpression;

            if (expr != null)
            {
                if (expr.Code == ILCode.Br || expr.Code == ILCode.Leave)
                {
                    ILLabel target = (ILLabel)expr.Operand;
                    // Early exit - same try-block
                    if (GetParents(expr).OfType <ILTryCatchBlock>().FirstOrDefault() == GetParents(target).OfType <ILTryCatchBlock>().FirstOrDefault())
                    {
                        return(Enter(target, visitedNodes));
                    }
                    // Make sure we are not entering any try-block
                    var srcTryBlocks = GetParents(expr).OfType <ILTryCatchBlock>().Reverse().ToList();
                    var dstTryBlocks = GetParents(target).OfType <ILTryCatchBlock>().Reverse().ToList();
                    // Skip blocks that we are already in
                    int i = 0;
                    while (i < srcTryBlocks.Count && i < dstTryBlocks.Count && srcTryBlocks[i] == dstTryBlocks[i])
                    {
                        i++;
                    }
                    if (i == dstTryBlocks.Count)
                    {
                        return(Enter(target, visitedNodes));
                    }
                    else
                    {
                        ILTryCatchBlock dstTryBlock = dstTryBlocks[i];
                        // Check that the goto points to the start
                        ILTryCatchBlock current = dstTryBlock;
                        while (current != null)
                        {
                            foreach (ILNode n in current.TryBlock.Body)
                            {
                                if (n is ILLabel)
                                {
                                    if (n == target)
                                    {
                                        return(dstTryBlock);
                                    }
                                }
                                else if (!n.Match(ILCode.Nop))
                                {
                                    current = n as ILTryCatchBlock;
                                    break;
                                }
                            }
                        }
                        return(null);
                    }
                }
                else if (expr.Code == ILCode.Nop)
                {
                    return(Exit(expr, visitedNodes));
                }
                else if (expr.Code == ILCode.LoopOrSwitchBreak)
                {
                    ILNode breakBlock = GetParents(expr).First(n => n is ILWhileLoop || n is ILSwitch);
                    return(Exit(breakBlock, new HashSet <ILNode>()
                    {
                        expr
                    }));
                }
                else if (expr.Code == ILCode.LoopContinue)
                {
                    ILNode continueBlock = GetParents(expr).First(n => n is ILWhileLoop);
                    return(Enter(continueBlock, new HashSet <ILNode>()
                    {
                        expr
                    }));
                }
                else
                {
                    return(expr);
                }
            }

            ILBlock block = node as ILBlock;

            if (block != null)
            {
                if (block.EntryGoto != null)
                {
                    return(Enter(block.EntryGoto, visitedNodes));
                }
                else if (block.Body.Count > 0)
                {
                    return(Enter(block.Body[0], visitedNodes));
                }
                else
                {
                    return(Exit(block, visitedNodes));
                }
            }

            ILCondition cond = node as ILCondition;

            if (cond != null)
            {
                return(cond.Condition);
            }

            ILWhileLoop loop = node as ILWhileLoop;

            if (loop != null)
            {
                if (loop.Condition != null)
                {
                    return(loop.Condition);
                }
                else
                {
                    return(Enter(loop.BodyBlock, visitedNodes));
                }
            }

            ILTryCatchBlock tryCatch = node as ILTryCatchBlock;

            if (tryCatch != null)
            {
                return(tryCatch);
            }

            ILSwitch ilSwitch = node as ILSwitch;

            if (ilSwitch != null)
            {
                return(ilSwitch.Condition);
            }

            throw new NotSupportedException(node.GetType().ToString());
        }