public JSTryCatchBlock TranslateNode(ILTryCatchBlock tcb) { var body = TranslateNode(tcb.TryBlock); JSVariable catchVariable = null; JSBlockStatement catchBlock = null; JSBlockStatement finallyBlock = null; if (tcb.CatchBlocks.Count > 0) { var pairs = new List<KeyValuePair<JSExpression, JSStatement>>(); catchVariable = DeclareVariable(new JSExceptionVariable(TypeSystem, ThisMethodReference)); bool foundUniversalCatch = false; foreach (var cb in tcb.CatchBlocks) { JSExpression pairCondition = null; if ( (cb.ExceptionType.FullName == "System.Exception") || (cb.ExceptionType.FullName == "System.Object") ) { // Bad IL sometimes contains entirely meaningless catch clauses. It's best to just ignore them. if ( (cb.Body.Count == 1) && (cb.Body[0] is ILExpression) && (((ILExpression)cb.Body[0]).Code == ILCode.Rethrow) ) { continue; } if (foundUniversalCatch) { Console.Error.WriteLine("Found multiple catch-all catch clauses. Any after the first will be ignored."); continue; } foundUniversalCatch = true; } else { if (foundUniversalCatch) throw new NotImplementedException("Catch-all clause must be last"); pairCondition = JSIL.CheckType(catchVariable, cb.ExceptionType); } var pairBody = TranslateBlock(cb.Body); if (cb.ExceptionVariable != null) { var excVariable = DeclareVariable(cb.ExceptionVariable, ThisMethodReference); pairBody.Statements.Insert( 0, new JSVariableDeclarationStatement(new JSBinaryOperatorExpression( JSOperator.Assignment, excVariable, catchVariable, cb.ExceptionVariable.Type )) ); } pairs.Add(new KeyValuePair<JSExpression, JSStatement>( pairCondition, pairBody )); } if (!foundUniversalCatch) pairs.Add(new KeyValuePair<JSExpression,JSStatement>( null, new JSExpressionStatement(new JSThrowExpression(catchVariable)) )); if ((pairs.Count == 1) && (pairs[0].Key == null)) catchBlock = new JSBlockStatement( pairs[0].Value ); else catchBlock = new JSBlockStatement( JSIfStatement.New(pairs.ToArray()) ); } if (tcb.FinallyBlock != null) finallyBlock = TranslateNode(tcb.FinallyBlock); if (tcb.FaultBlock != null) { if (catchBlock != null) throw new Exception("A try block cannot have both a catch block and a fault block"); catchVariable = DeclareVariable(new JSExceptionVariable(TypeSystem, ThisMethodReference)); catchBlock = new JSBlockStatement(TranslateBlock(tcb.FaultBlock.Body)); catchBlock.Statements.Add(new JSExpressionStatement(new JSThrowExpression(catchVariable))); } return new JSTryCatchBlock( body, catchVariable, catchBlock, finallyBlock ); }
void ValidateCatchBlock(ILTryCatchBlock.CatchBlock catchBlock) { if (catchBlock.ExceptionType == null || catchBlock.ExceptionType.TypeName != "Exception") throw new SymbolicAnalysisFailedException(); if (catchBlock.Body.Count != 3) throw new SymbolicAnalysisFailedException(); int stateID; if (!(MatchStateAssignment(catchBlock.Body[0], out stateID) && stateID == finalState)) throw new SymbolicAnalysisFailedException(); IMethod setExceptionMethod; ILExpression builderExpr, exceptionExpr; if (!catchBlock.Body[1].Match(ILCode.Call, out setExceptionMethod, out builderExpr, out exceptionExpr)) throw new SymbolicAnalysisFailedException(); if (!(setExceptionMethod.Name == "SetException" && IsBuilderFieldOnThis(builderExpr) && exceptionExpr.MatchLdloc(catchBlock.ExceptionVariable))) throw new SymbolicAnalysisFailedException(); ILLabel label; if (!(catchBlock.Body[2].Match(ILCode.Leave, out label) && label == exitLabel)) throw new SymbolicAnalysisFailedException(); }
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 SymbolicAnalysisFailedException(); 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 SymbolicAnalysisFailedException(); int val = (int)expr.Arguments[0].Operand; if (val == 0) { newBody.Add(new ILExpression(ILCode.YieldBreak, null)); } else if (val == 1) { newBody.Add(MakeGoTo(labels, currentState)); } else { throw new SymbolicAnalysisFailedException(); } } else if (expr != null && expr.Code == ILCode.Ret) { if (expr.Arguments.Count != 1 || expr.Arguments[0].Code != ILCode.Ldc_I4) throw new SymbolicAnalysisFailedException(); // handle direct return (e.g. in release builds) int val = (int)expr.Arguments[0].Operand; if (val == 0) { newBody.Add(new ILExpression(ILCode.YieldBreak, null)); } else if (val == 1) { newBody.Add(MakeGoTo(labels, currentState)); } else { throw new SymbolicAnalysisFailedException(); } } 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 SymbolicAnalysisFailedException(); StateRange stateRange; 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 SymbolicAnalysisFailedException(); newBody.Add(new ILExpression(ILCode.YieldBreak, null)); } else if (finallyMethodToStateRange.TryGetValue(method, out stateRange)) { // Call to Finally-method int index = stateChanges.FindIndex(ss => stateRange.Contains(ss.NewState)); if (index < 0) throw new SymbolicAnalysisFailedException(); ILLabel label = new ILLabel(); label.Name = "JumpOutOfTryFinally" + stateChanges[index].NewState; 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)); }
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; }
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; IField loadedField; if (!cachedStateInit.Match(ILCode.Ldfld, out loadedField, out instanceExpr) || loadedField.ResolveFieldWithinSameModule() != 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)) IMethod 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(); }
void AnalyzeMoveNext() { ILBlock ilMethod = CreateILAst(moveNextMethod); if (ilMethod.Body.Count != 6) throw new SymbolicAnalysisFailedException(); mainTryCatch = ilMethod.Body[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[1] as ILLabel; if (setResultAndExitLabel == null) throw new SymbolicAnalysisFailedException(); if (!MatchStateAssignment(ilMethod.Body[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[3].Match(ILCode.Call, out setResultMethod, out builderExpr, out resultExpr)) throw new SymbolicAnalysisFailedException(); } else { if (!ilMethod.Body[3].Match(ILCode.Call, out setResultMethod, out builderExpr)) throw new SymbolicAnalysisFailedException(); } if (!(setResultMethod.Name == "SetResult" && IsBuilderFieldOnThis(builderExpr))) throw new SymbolicAnalysisFailedException(); exitLabel = ilMethod.Body[4] as ILLabel; if (exitLabel == null) throw new SymbolicAnalysisFailedException(); }
public TryCatchNode ConvertTryCatch(ILTryCatchBlock ilTryCatch) { TryCatchNode tryCatch = new TryCatchNode(); Block tryBlock = new Block(); tryBlock.Childs.AddRange(SplitToBasicBlocks(ilTryCatch.TryBlock)); tryBlock.MoveTo(tryCatch); Block finallyBlock = new Block(); if (ilTryCatch.FinallyBlock != null) { finallyBlock.Childs.AddRange(SplitToBasicBlocks(ilTryCatch.FinallyBlock)); } finallyBlock.MoveTo(tryCatch); foreach(ILTryCatchBlock.CatchBlock cb in ilTryCatch.CatchBlocks) { tryCatch.Types.Add(cb.ExceptionType); Block catchBlock = new Block(); catchBlock.Childs.AddRange(SplitToBasicBlocks(cb.Body)); catchBlock.MoveTo(tryCatch); } return tryCatch; }
protected virtual ILTryCatchBlock VisitTryCatchBlock(ILTryCatchBlock tryCatchBlock) { foreach (var child in tryCatchBlock.GetChildren()) Visit(child); return tryCatchBlock; }