Example #1
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);
		}
Example #2
0
        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;
                    FieldDef 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, cachedStateVar);
                    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;
        }
Example #3
0
		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);
			finallyMethodToStateRange = rangeAnalysis.finallyMethodToStateRange;
			
			// Now look at the finally blocks:
			foreach (var tryFinally in ilMethod.GetSelfAndChildrenRecursive<ILTryCatchBlock>()) {
				var range = rangeAnalysis.ranges[tryFinally.TryBlock.Body[0]];
				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 || finallyMethodToStateRange.ContainsKey(mdef))
					throw new SymbolicAnalysisFailedException();
				finallyMethodToStateRange.Add(mdef, range);
			}
			rangeAnalysis = null;
		}
Example #4
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));
            }
        }