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