Esempio n. 1
0
		void AnalyzeStateMachine(ILBlock block)
		{
			var body = block.Body;
			if (body.Count == 0)
				throw new SymbolicAnalysisFailedException();
			if (DetectDoFinallyBodies(body)) {
				body.RemoveAt(0);
				if (body.Count == 0)
					throw new SymbolicAnalysisFailedException();
			}
			StateRangeAnalysis rangeAnalysis = new StateRangeAnalysis(body[0], StateRangeAnalysisMode.AsyncMoveNext, stateField, cachedStateVar);
			int bodyLength = block.Body.Count;
			int pos = rangeAnalysis.AssignStateRanges(body, bodyLength);
			rangeAnalysis.EnsureLabelAtPos(body, ref pos, ref bodyLength);
			
			var labelStateRangeMapping = rangeAnalysis.CreateLabelRangeMapping(body, pos, bodyLength);
			newTopLevelBody = ConvertBody(body, pos, bodyLength, labelStateRangeMapping);
			newTopLevelBody.Insert(0, MakeGoTo(labelStateRangeMapping, initialState));
			newTopLevelBody.Add(setResultAndExitLabel);
			if (methodType == AsyncMethodType.TaskOfT) {
				newTopLevelBody.Add(new ILExpression(ILCode.Ret, null, resultExpr));
			} else {
				newTopLevelBody.Add(new ILExpression(ILCode.Ret, null));
			}
		}
        List <ILNode> ConvertBody(List <ILNode> body, int startPos, int bodyLength, LabelRangeMapping mapping)
        {
            List <ILNode> newBody = new List <ILNode>();

            // Copy all instructions from the old body to newBody.
            for (int pos = startPos; pos < bodyLength; pos++)
            {
                ILTryCatchBlock tryCatchBlock = body[pos] as ILTryCatchBlock;
                ILExpression    expr          = body[pos] as ILExpression;
                if (expr != null && expr.Code == ILCode.Leave && expr.Operand == exitLabel)
                {
                    ILVariable      awaiterVar;
                    FieldDefinition awaiterField;
                    int             targetStateID;
                    HandleAwait(newBody, out awaiterVar, out awaiterField, out targetStateID);
                    MarkAsGeneratedVariable(awaiterVar);
                    newBody.Add(new ILExpression(ILCode.Await, null, new ILExpression(ILCode.Ldloca, awaiterVar)));
                    newBody.Add(MakeGoTo(mapping, targetStateID));
                }
                else if (tryCatchBlock != null)
                {
                    ILTryCatchBlock newTryCatchBlock = new ILTryCatchBlock();
                    var             tryBody          = tryCatchBlock.TryBlock.Body;
                    if (tryBody.Count == 0)
                    {
                        throw new SymbolicAnalysisFailedException();
                    }
                    StateRangeAnalysis rangeAnalysis = new StateRangeAnalysis(tryBody[0], StateRangeAnalysisMode.AsyncMoveNext, stateField);
                    int tryBodyLength = tryBody.Count;
                    int posInTryBody  = rangeAnalysis.AssignStateRanges(tryBody, tryBodyLength);
                    rangeAnalysis.EnsureLabelAtPos(tryBody, ref posInTryBody, ref tryBodyLength);

                    var mappingInTryBlock = rangeAnalysis.CreateLabelRangeMapping(tryBody, posInTryBody, tryBodyLength);
                    var newTryBody        = ConvertBody(tryBody, posInTryBody, tryBodyLength, mappingInTryBlock);
                    newTryBody.Insert(0, MakeGoTo(mappingInTryBlock, initialState));

                    // If there's a label at the beginning of the state dispatcher, copy that
                    if (posInTryBody > 0 && tryBody.FirstOrDefault() is ILLabel)
                    {
                        newTryBody.Insert(0, tryBody.First());
                    }

                    newTryCatchBlock.TryBlock    = new ILBlock(newTryBody);
                    newTryCatchBlock.CatchBlocks = new List <ILTryCatchBlock.CatchBlock>(tryCatchBlock.CatchBlocks);
                    newTryCatchBlock.FaultBlock  = tryCatchBlock.FaultBlock;
                    if (tryCatchBlock.FinallyBlock != null)
                    {
                        newTryCatchBlock.FinallyBlock = new ILBlock(ConvertFinally(tryCatchBlock.FinallyBlock.Body));
                    }

                    newBody.Add(newTryCatchBlock);
                }
                else
                {
                    newBody.Add(body[pos]);
                }
            }
            return(newBody);
        }
        void ConstructExceptionTable()
        {
            disposeMethod = enumeratorType.Methods.FirstOrDefault(m => m.Name == "System.IDisposable.Dispose");
            ILBlock ilMethod = CreateILAst(disposeMethod);

            var rangeAnalysis = new StateRangeAnalysis(ilMethod.Body[0], StateRangeAnalysisMode.IteratorDispose, stateField);

            rangeAnalysis.AssignStateRanges(ilMethod.Body, ilMethod.Body.Count);
            finallyMethodToStateInterval = rangeAnalysis.finallyMethodToStateInterval;

            // Now look at the finally blocks:
            foreach (var tryFinally in ilMethod.GetSelfAndChildrenRecursive <ILTryCatchBlock>())
            {
                Interval interval    = rangeAnalysis.ranges[tryFinally.TryBlock.Body[0]].ToEnclosingInterval();
                var      finallyBody = tryFinally.FinallyBlock.Body;
                if (finallyBody.Count != 2)
                {
                    throw new SymbolicAnalysisFailedException();
                }
                ILExpression call = finallyBody[0] as ILExpression;
                if (call == null || call.Code != ILCode.Call || call.Arguments.Count != 1)
                {
                    throw new SymbolicAnalysisFailedException();
                }
                if (!call.Arguments[0].MatchThis())
                {
                    throw new SymbolicAnalysisFailedException();
                }
                if (!finallyBody[1].Match(ILCode.Endfinally))
                {
                    throw new SymbolicAnalysisFailedException();
                }

                MethodDefinition mdef = GetMethodDefinition(call.Operand as MethodReference);
                if (mdef == null || finallyMethodToStateInterval.ContainsKey(mdef))
                {
                    throw new SymbolicAnalysisFailedException();
                }
                finallyMethodToStateInterval.Add(mdef, interval);
            }
            rangeAnalysis = null;
        }
        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
            }
            // 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 SymbolicAnalysisFailedException();
            }

            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);
        }