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