internal static Block CreateTryFinallyBlock(Method method, Block tryBody, Block finallyBody) { Contract.Requires(method != null); Contract.Requires(tryBody != null); Contract.Requires(finallyBody != null); if (method.ExceptionHandlers == null) method.ExceptionHandlers = new ExceptionHandlerList(); Block result = new Block(new StatementList()); Block afterFinally = new Block(new StatementList()); tryBody.Statements.Add(new Branch(null, afterFinally, false, true, true)); finallyBody.Statements.Add(new EndFinally()); result.Statements.Add(tryBody); result.Statements.Add(finallyBody); result.Statements.Add(afterFinally); ExceptionHandler fb = new ExceptionHandler(); fb.TryStartBlock = tryBody; fb.BlockAfterTryEnd = finallyBody; fb.HandlerStartBlock = finallyBody; fb.BlockAfterHandlerEnd = afterFinally; fb.HandlerType = NodeType.Finally; method.ExceptionHandlers.Add(fb); return result; }
private void EmitRecursionGuardAroundChecks(Method method, Block newBody, StatementList checks) { StatementList stmts = new StatementList(); Block preconditionsStart = new Block(stmts); Block finallyStart = null; Block finallyEnd = new Block(); // branch target for skipping the checks // test if we are an auto property and need to disable the check if we are in construction if (this.ReentrancyFlag != null && (method.IsPropertyGetter || method.IsPropertySetter) && HelperMethods.IsAutoPropertyMember(method) && !method.IsStatic) { newBody.Statements.Add(new Branch(new MemberBinding(method.ThisParameter, this.ReentrancyFlag), finallyEnd)); } // don't add try finally if there are no precondition or if the method is a constructor (peverify issue) if (NeedsRecursionGuard(method, checks)) { // emit recursion check finallyStart = new Block(new StatementList()); // if (insideContractEvaluation > $recursionGuard$) goto finallyEnd // try { // insideContractEvaluation++; // checks; // leave; // } finally { // insideContractEvaluation--; // } // // SPECIAL CASE for auto properties where we made invariants into pre/post, we need to avoid the check if we are // evaluating the invariant. // // if (this.$evaluatingInvariant || insideContractEvaluation > $recursionGuard$) goto finallyEnd // newBody.Statements.Add(new Branch(new BinaryExpression(new MemberBinding(null, this.runtimeContracts.InContractEvaluationField), new Literal(this.runtimeContracts.RecursionGuardCountFor(method), SystemTypes.Int32), NodeType.Gt), finallyEnd)); stmts.Add(new AssignmentStatement(new MemberBinding(null, this.runtimeContracts.InContractEvaluationField), new BinaryExpression(new MemberBinding(null, this.runtimeContracts.InContractEvaluationField), Literal.Int32One, NodeType.Add))); stmts.Add(new Block(checks)); var leave = new Branch(); leave.Target = finallyEnd; leave.LeavesExceptionBlock = true; stmts.Add(leave); finallyStart.Statements.Add(new AssignmentStatement(new MemberBinding(null, this.runtimeContracts.InContractEvaluationField), new BinaryExpression(new MemberBinding(null, this.runtimeContracts.InContractEvaluationField), Literal.Int32One, NodeType.Sub))); finallyStart.Statements.Add(new EndFinally()); } else { stmts.Add(new Block(checks)); } newBody.Statements.Add(preconditionsStart); if (finallyStart != null) { newBody.Statements.Add(finallyStart); var finallyHandler = new ExceptionHandler(); finallyHandler.TryStartBlock = preconditionsStart; finallyHandler.BlockAfterTryEnd = finallyStart; finallyHandler.HandlerStartBlock = finallyStart; finallyHandler.BlockAfterHandlerEnd = finallyEnd; finallyHandler.HandlerType = NodeType.Finally; method.ExceptionHandlers.Add(finallyHandler); } // branch target for skipping the checks newBody.Statements.Add(finallyEnd); }
private static Statement WrapTryCatch(Method method, Statement statement) { Block afterCatches = new Block(new StatementList()); Block tryBlock = new Block(new StatementList()); Block blockAfterTryBody = new Block(null); tryBlock.Statements.Add(statement); tryBlock.Statements.Add(new Branch(null, afterCatches, false, true, true)); tryBlock.Statements.Add(blockAfterTryBody); Block catchBlock = new Block(new StatementList()); // emit code that pops the exception and fools fxcop Block branchTargetToFoolFxCop = new Block(null); var branch = new Branch(new Expression(NodeType.Pop), branchTargetToFoolFxCop); SourceContext hiddenContext = new SourceContext(HiddenDocument.Document); branch.SourceContext = hiddenContext; catchBlock.Statements.Add(branch); var rethrowStatement = new Throw(); rethrowStatement.SourceContext = hiddenContext; rethrowStatement.NodeType = NodeType.Rethrow; catchBlock.Statements.Add(rethrowStatement); catchBlock.Statements.Add(branchTargetToFoolFxCop); var leave = new Branch(null, afterCatches, false, true, true); leave.SourceContext = hiddenContext; catchBlock.Statements.Add(leave); Block tryCatch = new Block(new StatementList()); tryCatch.Statements.Add(tryBlock); tryCatch.Statements.Add(catchBlock); tryCatch.Statements.Add(afterCatches); if (method.ExceptionHandlers == null) method.ExceptionHandlers = new ExceptionHandlerList(); ExceptionHandler exHandler = new ExceptionHandler(); exHandler.TryStartBlock = tryBlock; exHandler.BlockAfterTryEnd = blockAfterTryBody; exHandler.HandlerStartBlock = catchBlock; exHandler.BlockAfterHandlerEnd = afterCatches; exHandler.FilterType = SystemTypes.Exception; exHandler.HandlerType = NodeType.Catch; method.ExceptionHandlers.Add(exHandler); return tryCatch; }
//[ContractVerification(true)] private void VisitMethodInternal(Method method) { if (method.IsAbstract || IsRewriterGenerated(method)) { return; } Block origBody = method.Body; if (origBody == null || origBody.Statements == null || origBody.Statements.Count == 0) return; #if DEBUG if (!RewriteHelper.PostNormalizedFormat(method)) throw new RewriteException("The method body for '" + method.FullName + "' is not structured correctly"); #endif #region Rewrite all Assert and Assume methods // Happens in-place so any modifications made are persisted even if nothing else is done to method RewriteAssertAssumeAndCallSiteRequires raa = new RewriteAssertAssumeAndCallSiteRequires(this.AssertAssumeRewriteMethodTable, this.runtimeContracts, this.contractEmitFlags, this, method); method.Body = raa.VisitBlock(method.Body); #endregion Rewrite all Assert and Assume methods (if user specified) #region Bail out early if publicSurfaceOnly and method is not visible (but not for contract validators!) or contract abbreviator if (this.runtimeContracts.PublicSurfaceOnly && !IsCallableFromOutsideAssembly(method) && !ContractNodes.IsValidatorMethod(method) || ContractNodes.IsAbbreviatorMethod(method)) return; #endregion int oldLocalUniqueNameCounter = 0; List<Block> contractInitializationBlocks = new List<Block>(); Block postPreambleBlock = null; Dictionary<TypeNode, Local> closureLocals = new Dictionary<TypeNode, Local>(); Block oldExpressionPreStateValues = new Block(new StatementList()); #region Gather pre- and postconditions from supertypes and from method contract // Create lists of unique preconditions and postconditions. List<Requires> preconditions = new List<Requires>(); List<Ensures> postconditions = new List<Ensures>(); List<Ensures> asyncPostconditions = new List<Ensures>(); RequiresList validations = null; // For postconditions, wrap all parameters within an old expression (if they are not already within one) var wrap = new WrapParametersInOldExpressions(); EmitAsyncClosure asyncBuilder = null; #region Copy the method's contracts. if (method.Contract != null && (method.Contract.RequiresCount > 0 || method.Contract.EnsuresCount > 0 || method.Contract.ValidationsCount > 0 || method.Contract.AsyncEnsuresCount > 0)) { // Use duplicate of contract because it is going to get modified from processing Old(...) and // Result(). Can't have the modified contract get inherited (or else the Old(...) processing // will not work properly.) // Can't use MethodContract.CopyFrom or HelperMethods.DuplicateMethodBodyAndContract // because they assume the new contract is in a different method. Plus the latter duplicates // any closure classes needed for anonymous delegates in the contract. MethodContract mc = HelperMethods.DuplicateContractAndClosureParts(method, method, this.runtimeContracts.ContractNodes, true); validations = mc.Validations; if (mc != null) { contractInitializationBlocks.Add(mc.ContractInitializer); postPreambleBlock = mc.PostPreamble; RecordEnsures(method, ref oldLocalUniqueNameCounter, contractInitializationBlocks, closureLocals, oldExpressionPreStateValues, postconditions, asyncPostconditions, wrap, ref asyncBuilder, mc); } } #endregion Copy the method's contracts. #region Search the class hierarchy for overridden methods to propagate their contracts. if (this.Emit(RuntimeContractEmitFlags.InheritContracts) && method.OverridesBaseClassMember && method.DeclaringType != null && method.OverriddenMethod != null) { for (TypeNode super = method.DeclaringType.BaseType; super != null; super = HelperMethods.DoesInheritContracts(super)?super.BaseType:null) { var baseMethod = super.GetImplementingMethod(method.OverriddenMethod, false); Method baseContractMethod = HelperMethods.GetContractMethod(baseMethod); if (baseContractMethod != null) { MethodContract mc = HelperMethods.DuplicateContractAndClosureParts(method, baseContractMethod, this.runtimeContracts.ContractNodes, false); RewriteHelper.ReplacePrivateFieldsThatHavePublicProperties(method.DeclaringType, super, mc, this.rewriterNodes); if (mc != null) { contractInitializationBlocks.Add(mc.ContractInitializer); // can't have post preambles in overridden methods, since they cannot be constructors. // only add requires if baseMethod is the root method if (mc.Requires != null && baseMethod.OverriddenMethod == null) { foreach (RequiresPlain requires in mc.Requires) { if (!EmitRequires(requires, this.skipQuantifiers)) continue; // Debug.Assert(!preconditions.Contains(requires)); preconditions.Add(requires); } } RecordEnsures(method, ref oldLocalUniqueNameCounter, contractInitializationBlocks, closureLocals, oldExpressionPreStateValues, postconditions, asyncPostconditions, wrap, ref asyncBuilder, mc); } } // bail out if this base type does not inherit contracts if (!HelperMethods.DoesInheritContracts(super)) break; } } #endregion Search the class hierarchy for overridden methods to propagate their contracts. #region Propagate explicit interface method contracts. if (this.Emit(RuntimeContractEmitFlags.InheritContracts) && method.ImplementedInterfaceMethods != null) { foreach (Method interfaceMethod in method.ImplementedInterfaceMethods) { if (interfaceMethod != null) { Method contractMethod = HelperMethods.GetContractMethod(interfaceMethod); if (contractMethod != null) { // if null, then no contract for this interface method // Maybe it would be easier to just duplicate the entire method and then pull the // initialization code from the duplicate? MethodContract mc=HelperMethods.DuplicateContractAndClosureParts(method, contractMethod, this.runtimeContracts.ContractNodes, false); if (mc != null) { contractInitializationBlocks.Add(mc.ContractInitializer); // can't have post preambles in interface methods (not constructors) if (mc.Requires != null) { foreach (RequiresPlain requires in mc.Requires) { if (!EmitRequires(requires, this.skipQuantifiers)) continue; // Debug.Assert(!preconditions.Contains(requires)); preconditions.Add(requires); } } RecordEnsures(method, ref oldLocalUniqueNameCounter, contractInitializationBlocks, closureLocals, oldExpressionPreStateValues, postconditions, asyncPostconditions, wrap, ref asyncBuilder, mc); } } } } } #endregion Propagate explicit interface method contracts. #region Propagate implicit interface method contracts. if (this.Emit(RuntimeContractEmitFlags.InheritContracts) && method.ImplicitlyImplementedInterfaceMethods != null) { foreach (Method interfaceMethod in method.ImplicitlyImplementedInterfaceMethods) { if (interfaceMethod != null) { Method contractMethod = HelperMethods.GetContractMethod(interfaceMethod); if (contractMethod != null) { // if null, then no contract for this method // Maybe it would be easier to just duplicate the entire method and then pull the // initialization code from the duplicate? MethodContract mc = HelperMethods.DuplicateContractAndClosureParts(method, contractMethod, this.runtimeContracts.ContractNodes, false); if (mc != null) { contractInitializationBlocks.Add(mc.ContractInitializer); // can't have post preambles in implicit interface method implementations, as they are not constructors. if (mc.Requires != null) { foreach (RequiresPlain requires in mc.Requires) { if (!EmitRequires(requires, this.skipQuantifiers)) continue; // Debug.Assert(!preconditions.Contains(requires)); preconditions.Add(requires); } } RecordEnsures(method, ref oldLocalUniqueNameCounter, contractInitializationBlocks, closureLocals, oldExpressionPreStateValues, postconditions, asyncPostconditions, wrap, ref asyncBuilder, mc); } } } } } #endregion Propagate implicit interface method contracts. #endregion Gather pre- and postconditions from supertypes and from method contract #region Return if there is nothing to do if (preconditions.Count < 1 && postconditions.Count < 1 && asyncPostconditions.Count < 1 && (validations == null || validations.Count == 0) && this.InvariantMethod == null) return; #endregion Return if there is nothing to do #region Change all short branches to long because code modifications can add code { // REVIEW: This does *not* remove any short branches in the contracts. // I think that is okay, but we should think about it. RemoveShortBranches rsb = new RemoveShortBranches(); rsb.VisitBlock(method.Body); } #endregion Change all short branches to long because code modifications can add code #region Modify method body to change all returns into assignments and branch to unified exit // now modify the method body to change all return statements // into assignments to the local "result" and a branch to a // block at the end that can check the post-condition using // "result" and then finally return it. // [MAF] we do it later once we know if the branches are leaves or just branch. Local result = null; if (!HelperMethods.IsVoidType(method.ReturnType)) { // don't write huge names. The debugger chokes on them and shows no locals at all. // result = new Local(Identifier.For("Contract.Result<" + typeName + ">()"), method.ReturnType); result = new Local(Identifier.For("Contract.Result()"), method.ReturnType); if (method.LocalList == null) { method.LocalList = new LocalList(); } method.LocalList.Add(result); } Block newExit = new Block(new StatementList()); #endregion Modify method body to change all returns into assignments and branch to unified exit #region Create the new method's body Block newBody = new Block(new StatementList()); newBody.HasLocals = true; #endregion Create the new method's body #region If there had been any closure initialization code, put it first in the new body if (0 < contractInitializationBlocks.Count) { foreach (Block b in contractInitializationBlocks) { newBody.Statements.Add(b); } } #endregion EmitInterleavedValidationsAndRequires(method, preconditions, validations, newBody); #region Turn off invariant checking until the end Local oldReEntrancyFlagLocal = null; if (MethodShouldHaveInvariantChecked(method) && this.ReentrancyFlag != null && BodyHasCalls(method.Body)) { oldReEntrancyFlagLocal = new Local(SystemTypes.Boolean); newBody.Statements.Add(new Block(new StatementList( new AssignmentStatement(oldReEntrancyFlagLocal, new MemberBinding(method.ThisParameter, this.ReentrancyFlag)), new AssignmentStatement(new MemberBinding(method.ThisParameter, this.ReentrancyFlag), Literal.True)))); } #endregion #region Put all of the collected initializations from "old" expressions into the method if (oldExpressionPreStateValues.Statements.Count > 0) { newBody.Statements.Add(oldExpressionPreStateValues); } #endregion // if there are preamble blocks we need to move them from the origBody in case we will wrap a try-catch var preambleIndex = 0; while (origBody.Statements.Count > preambleIndex && origBody.Statements[preambleIndex] is PreambleBlock) { newBody.Statements.Add(origBody.Statements[preambleIndex]); origBody.Statements[preambleIndex] = null; preambleIndex++; } Block newBodyBlock = new Block(new StatementList()); newBody.Statements.Add(newBodyBlock); // placeholder for eventual body newBody.Statements.Add(newExit); #region Replace "result" in postconditions (both for method return and out parameters) if (result != null) { foreach (Ensures e in postconditions) { if (e == null) continue; ReplaceResult repResult = new ReplaceResult(method, result, this.assemblyBeingRewritten); repResult.Visit(e); // now need to initialize closure result fields foreach (var target in repResult.NecessaryResultInitialization(closureLocals)) { newBody.Statements.Add(new AssignmentStatement(target, result)); } } } #endregion #region Emit potential post preamble block (from contract duplicate) in constructors if (postPreambleBlock != null) { newBody.Statements.Add(postPreambleBlock); } #endregion #region Emit normal postconditions SourceContext lastEnsuresSourceContext = default(SourceContext); bool hasLastEnsuresContext = false; bool containsExceptionalPostconditions = false; var ensuresChecks = new StatementList(); foreach (Ensures e in postconditions) { // Exceptional postconditions are handled separately. if (e is EnsuresExceptional) { containsExceptionalPostconditions = true; continue; } lastEnsuresSourceContext = e.SourceContext; hasLastEnsuresContext = true; // call Contract.RewriterEnsures Method ensMethod = this.runtimeContracts.EnsuresMethod; ExpressionList args = new ExpressionList(); args.Add(e.PostCondition); if (e.UserMessage != null) args.Add(e.UserMessage); else args.Add(Literal.Null); if (e.SourceConditionText != null) { args.Add(e.SourceConditionText); } else { args.Add(Literal.Null); } ensuresChecks.Add(new ExpressionStatement( new MethodCall(new MemberBinding(null, ensMethod), args, NodeType.Call, SystemTypes.Void), e.SourceContext)); } this.cleanUpCodeCoverage.VisitStatementList(ensuresChecks); EmitRecursionGuardAroundChecks(method, newBody, ensuresChecks); #endregion Normal postconditions #region Emit object invariant if (MethodShouldHaveInvariantChecked(method)) { // Now turn checking on by restoring old reentrancy flag if (this.ReentrancyFlag != null && oldReEntrancyFlagLocal != null) { newBody.Statements.Add(new AssignmentStatement(new MemberBinding(method.ThisParameter, this.ReentrancyFlag), oldReEntrancyFlagLocal)); } var callType = (method is InstanceInitializer) || this.InvariantMethod.DeclaringType.IsValueType ? NodeType.Call : NodeType.Callvirt; // just add a call to the already existing invariant method, "this.InvariantMethod();" // all of the processing needed is done as part of VisitClass newBody.Statements.Add( new ExpressionStatement( new MethodCall( new MemberBinding(method.ThisParameter, this.InvariantMethod), null, callType, SystemTypes.Void))); } #endregion Object invariant #region Emit exceptional postconditions ReplaceReturns rr; if (containsExceptionalPostconditions) { // -- The following code comes from System.Compiler.Normalizer.CreateTryCatchBlock -- // The tryCatch holds the try block, the catch blocks, and an empty block that is the // target of an unconditional branch for normal execution to go from the try block // around the catch blocks. if (method.ExceptionHandlers == null) method.ExceptionHandlers = new ExceptionHandlerList(); Block afterCatches = new Block(new StatementList()); Block tryCatch = newBodyBlock; Block tryBlock = new Block(new StatementList()); tryBlock.Statements.Add(origBody); rr = new ReplaceReturns(result, newExit, leaveExceptionBody:true); rr.Visit(origBody); tryBlock.Statements.Add(new Branch(null, afterCatches, false, true, true)); // the EH needs to have a pointer to this block so the writer can // calculate the length of the try block. So it should be the *last* // thing in the try body. Block blockAfterTryBody = new Block(null); tryBlock.Statements.Add(blockAfterTryBody); tryCatch.Statements.Add(tryBlock); for (int i = 0, n = postconditions.Count; i < n; i++) { // Normal postconditions are handled separately. EnsuresExceptional e = postconditions[i] as EnsuresExceptional; if (e == null) continue; // The catchBlock contains the catchBody, and then // an empty block that is used in the EH. Block catchBlock = new Block(new StatementList()); Local l = new Local(e.Type); Throw rethrow = new Throw(); rethrow.NodeType = NodeType.Rethrow; // call Contract.RewriterEnsures ExpressionList args = new ExpressionList(); args.Add(e.PostCondition); if (e.UserMessage != null) args.Add(e.UserMessage); else args.Add(Literal.Null); if (e.SourceConditionText != null) { args.Add(e.SourceConditionText); } else { args.Add(Literal.Null); } args.Add(l); var checks = new StatementList(); checks.Add(new ExpressionStatement( new MethodCall(new MemberBinding(null, this.runtimeContracts.EnsuresOnThrowMethod), args, NodeType.Call, SystemTypes.Void), e.SourceContext)); catchBlock.Statements.Add(new AssignmentStatement(l, new Expression(NodeType.Pop), e.SourceContext)); this.cleanUpCodeCoverage.VisitStatementList(checks); EmitRecursionGuardAroundChecks(method, catchBlock, checks); #region Emit object invariant on EnsuresOnThrow check if (MethodShouldHaveInvariantChecked(method, inExceptionCase: true)) { // Now turn checking on by restoring old reentrancy flag if (this.ReentrancyFlag != null && oldReEntrancyFlagLocal != null) { catchBlock.Statements.Add(new AssignmentStatement(new MemberBinding(method.ThisParameter, this.ReentrancyFlag), oldReEntrancyFlagLocal)); } // just add a call to the already existing invariant method, "this.InvariantMethod();" // all of the processing needed is done as part of VisitClass catchBlock.Statements.Add( new ExpressionStatement( new MethodCall( new MemberBinding(method.ThisParameter, this.InvariantMethod), null, NodeType.Call, SystemTypes.Void))); } #endregion Object invariant catchBlock.Statements.Add(rethrow); // The last thing in each catch block is an empty block that is the target of // BlockAfterHandlerEnd in each exception handler. // It is used in the writer to determine the length of each catch block // so it should be the last thing added to each catch block. Block blockAfterHandlerEnd = new Block(new StatementList()); catchBlock.Statements.Add(blockAfterHandlerEnd); tryCatch.Statements.Add(catchBlock); // add information to the ExceptionHandlers of this method ExceptionHandler exHandler = new ExceptionHandler(); exHandler.TryStartBlock = origBody; exHandler.BlockAfterTryEnd = blockAfterTryBody; exHandler.HandlerStartBlock = catchBlock; exHandler.BlockAfterHandlerEnd = blockAfterHandlerEnd; exHandler.FilterType = l.Type; exHandler.HandlerType = NodeType.Catch; method.ExceptionHandlers.Add(exHandler); } tryCatch.Statements.Add(afterCatches); } else // no exceptional post conditions { newBodyBlock.Statements.Add(origBody); rr = new ReplaceReturns(result, newExit, leaveExceptionBody: false); rr.Visit(origBody); } #endregion Exceptional and finally postconditions #region Create a block for the return statement and insert it // this is the block that contains the return statements // it is (supposed to be) the single exit from the method // that way, anything that must always be done can be done // in this block Block returnBlock = new Block(new StatementList(1)); if (asyncPostconditions != null && asyncPostconditions.Count > 0) { asyncBuilder.AddAsyncPost(asyncPostconditions); var funcLocal = new Local(asyncBuilder.FuncCtor.DeclaringType); var ldftn = new UnaryExpression(new MemberBinding(null, asyncBuilder.CheckMethod), NodeType.Ldftn, CoreSystemTypes.IntPtr); returnBlock.Statements.Add(new AssignmentStatement(funcLocal, new Construct(new MemberBinding(null, asyncBuilder.FuncCtor), new ExpressionList(asyncBuilder.ClosureLocal, ldftn)))); returnBlock.Statements.Add(new AssignmentStatement(result, new MethodCall(new MemberBinding(result, asyncBuilder.ContinueWithMethod), new ExpressionList(funcLocal)))); } Statement returnStatement; if (!HelperMethods.IsVoidType(method.ReturnType)) { returnStatement = new Return(result); } else { returnStatement = new Return(); } if (hasLastEnsuresContext) { returnStatement.SourceContext = lastEnsuresSourceContext; } else { returnStatement.SourceContext = rr.LastReturnSourceContext; } returnBlock.Statements.Add(returnStatement); newBody.Statements.Add(returnBlock); #endregion method.Body = newBody; #region Make sure InitLocals is marked for this method // 15 April 2003 // Since each method has locals added to it, need to make sure this flag is // on. Otherwise, the generated code cannot pass peverify. if (!method.InitLocals) { method.InitLocals = true; //WriteToLog("Setting InitLocals for method: {0}", method.FullName); } #endregion }
public void AddAsyncPost(List<Ensures> asyncPostconditions) { var origBody = new Block(this.checkBody); origBody.HasLocals = true; var newBodyBlock = new Block(new StatementList()); newBodyBlock.HasLocals = true; var methodBody = new StatementList(); var methodBodyBlock = new Block(methodBody); methodBodyBlock.HasLocals = true; checkMethod.Body = methodBodyBlock; methodBody.Add(newBodyBlock); Block newExitBlock = new Block(); methodBody.Add(newExitBlock); #region Map closure locals to fields and initialize closure fields foreach (Ensures e in asyncPostconditions) { if (e == null) continue; this.Visit(e); if (this.forwarder != null) { this.forwarder.Visit(e); } ReplaceResult repResult = new ReplaceResult(this.checkMethod, this.originalResultLocal, this.parent.assemblyBeingRewritten); repResult.Visit(e); // now need to initialize closure result fields foreach (var target in repResult.NecessaryResultInitializationAsync(this.closureLocals)) { // note: target here methodBody.Add(new AssignmentStatement(target, this.originalResultLocal)); } } #endregion #region Emit normal postconditions SourceContext lastEnsuresSourceContext = default(SourceContext); bool hasLastEnsuresContext = false; bool containsExceptionalPostconditions = false; var ensuresChecks = new StatementList(); foreach (Ensures e in asyncPostconditions) { // Exceptional postconditions are handled separately. if (e is EnsuresExceptional) { containsExceptionalPostconditions = true; continue; } if (IsVoidTask()) break; // something is wrong in the original contract lastEnsuresSourceContext = e.SourceContext; hasLastEnsuresContext = true; // call Contract.RewriterEnsures Method ensMethod = this.parent.runtimeContracts.EnsuresMethod; ExpressionList args = new ExpressionList(); args.Add(e.PostCondition); if (e.UserMessage != null) args.Add(e.UserMessage); else args.Add(Literal.Null); if (e.SourceConditionText != null) { args.Add(e.SourceConditionText); } else { args.Add(Literal.Null); } ensuresChecks.Add(new ExpressionStatement( new MethodCall(new MemberBinding(null, ensMethod), args, NodeType.Call, SystemTypes.Void), e.SourceContext)); } this.parent.cleanUpCodeCoverage.VisitStatementList(ensuresChecks); this.parent.EmitRecursionGuardAroundChecks(this.checkMethod, methodBodyBlock, ensuresChecks); #endregion Normal postconditions #region Exceptional postconditions if (containsExceptionalPostconditions) { // Because async tasks wrap exceptions into Aggregate exceptions, we have to catch AggregateException // and iterate over the internal exceptions of the Aggregate. // We thus emit the following handler: // // catch(AggregateException ae) { // ae.Handle(this.CheckException); // rethrow; // } // // alternatively, if the Task has no Result, we emit // // var ae = t.Exception as AggregateException; // if (ae != null) { // ae.Handle(this.CheckException); // throw ae; // } var aggregateType = AggregateExceptionType.Value; var exnParameter = new Parameter(Identifier.For("e"), SystemTypes.Exception); this.checkExceptionMethod = new Method(this.closureClass, null, this.checkExceptionMethodId, new ParameterList(exnParameter), SystemTypes.Boolean, new Block(new StatementList())); this.checkExceptionMethod.Body.HasLocals = true; checkExceptionMethod.CallingConvention = CallingConventionFlags.HasThis; checkExceptionMethod.Flags |= MethodFlags.Public; this.closureClass.Members.Add(checkExceptionMethod); if (this.IsVoidTask()) { var blockAfterTest = new Block(); methodBody.Add(origBody); methodBody.Add(new Branch(new UnaryExpression(this.newResultLocal, NodeType.LogicalNot), blockAfterTest)); var funcType = Func2Type.Value; funcType = funcType.GetTemplateInstance(this.parent.assemblyBeingRewritten, SystemTypes.Exception, SystemTypes.Boolean); var handleMethod = aggregateType.GetMethod(Identifier.For("Handle"), funcType); var funcLocal = new Local(funcType); var ldftn = new UnaryExpression(new MemberBinding(null, this.checkExceptionMethod), NodeType.Ldftn, CoreSystemTypes.IntPtr); methodBody.Add(new AssignmentStatement(funcLocal, new Construct(new MemberBinding(null, funcType.GetConstructor(SystemTypes.Object, SystemTypes.IntPtr)), new ExpressionList(this.checkMethod.ThisParameter, ldftn)))); methodBody.Add(new ExpressionStatement(new MethodCall(new MemberBinding(this.newResultLocal, handleMethod), new ExpressionList(funcLocal)))); methodBody.Add(new Throw(this.newResultLocal)); methodBody.Add(blockAfterTest); } else { // The tryCatch holds the try block, the catch blocks, and an empty block that is the // target of an unconditional branch for normal execution to go from the try block // around the catch blocks. if (this.checkMethod.ExceptionHandlers == null) this.checkMethod.ExceptionHandlers = new ExceptionHandlerList(); Block afterCatches = new Block(new StatementList()); Block tryCatch = newBodyBlock; Block tryBlock = new Block(new StatementList()); tryBlock.Statements.Add(origBody); tryBlock.Statements.Add(new Branch(null, afterCatches, false, true, true)); // the EH needs to have a pointer to this block so the writer can // calculate the length of the try block. So it should be the *last* // thing in the try body. Block blockAfterTryBody = new Block(null); tryBlock.Statements.Add(blockAfterTryBody); tryCatch.Statements.Add(tryBlock); // The catchBlock contains the catchBody, and then // an empty block that is used in the EH. Block catchBlock = new Block(new StatementList()); Local l = new Local(aggregateType); Throw rethrow = new Throw(); rethrow.NodeType = NodeType.Rethrow; catchBlock.Statements.Add(new AssignmentStatement(l, new Expression(NodeType.Pop))); var funcType = Func2Type.Value; funcType = funcType.GetTemplateInstance(this.parent.assemblyBeingRewritten, SystemTypes.Exception, SystemTypes.Boolean); var handleMethod = aggregateType.GetMethod(Identifier.For("Handle"), funcType); var funcLocal = new Local(funcType); var ldftn = new UnaryExpression(new MemberBinding(null, this.checkExceptionMethod), NodeType.Ldftn, CoreSystemTypes.IntPtr); catchBlock.Statements.Add(new AssignmentStatement(funcLocal, new Construct(new MemberBinding(null, funcType.GetConstructor(SystemTypes.Object, SystemTypes.IntPtr)), new ExpressionList(this.checkMethod.ThisParameter, ldftn)))); catchBlock.Statements.Add(new ExpressionStatement(new MethodCall(new MemberBinding(l, handleMethod), new ExpressionList(funcLocal)))); // Handle method should return if all passes catchBlock.Statements.Add(rethrow); // The last thing in each catch block is an empty block that is the target of // BlockAfterHandlerEnd in each exception handler. // It is used in the writer to determine the length of each catch block // so it should be the last thing added to each catch block. Block blockAfterHandlerEnd = new Block(new StatementList()); catchBlock.Statements.Add(blockAfterHandlerEnd); tryCatch.Statements.Add(catchBlock); // add information to the ExceptionHandlers of this method ExceptionHandler exHandler = new ExceptionHandler(); exHandler.TryStartBlock = origBody; exHandler.BlockAfterTryEnd = blockAfterTryBody; exHandler.HandlerStartBlock = catchBlock; exHandler.BlockAfterHandlerEnd = blockAfterHandlerEnd; exHandler.FilterType = l.Type; exHandler.HandlerType = NodeType.Catch; this.checkMethod.ExceptionHandlers.Add(exHandler); tryCatch.Statements.Add(afterCatches); } EmitCheckExceptionBody(asyncPostconditions); } else // no exceptional post conditions { newBodyBlock.Statements.Add(origBody); } #endregion Exceptional and finally postconditions #region Create a block for the return statement and insert it // this is the block that contains the return statements // it is (supposed to be) the single exit from the method // that way, anything that must always be done can be done // in this block Statement returnStatement; if (this.IsVoidTask()) { returnStatement = new Return(); } else { returnStatement = new Return(this.newResultLocal); } if (hasLastEnsuresContext) { returnStatement.SourceContext = lastEnsuresSourceContext; } Block returnBlock = new Block(new StatementList(1)); returnBlock.Statements.Add(returnStatement); methodBody.Add(returnBlock); #endregion }
private void DuplicateFinallyBody ( ExceptionHandler eh, Block leave_block, out Block startBlock, out Block lastBlock ) { Hashtable/*<Block,Block>*/ orig2copy = new Hashtable(); Block hdlr_last_block; ISet/*<Block>*/ handler_body = GetFinallyBody(eh, out hdlr_last_block); foreach(Block orig_block in handler_body) { Block clone_block = dupVisitor.VisitBlock(orig_block); this.copy2orig.Add(clone_block, orig_block); orig2copy.Add(orig_block, clone_block); if (ControlFlowGraph.FINALLY_CLONE_DEBUG) { this.cfg.b2cloning_leave[clone_block] = leave_block; } /* Console.Out.WriteLine("cloning {0} -> {1}", CodePrinter.b2s(orig_block), CodePrinter.b2s(clone_block)); */ this.new_blocks.Add(clone_block); } CorrectBranchVisitor correctBranchVisitor = new CorrectBranchVisitor(orig2copy); foreach(Block orig_block in handler_body) { Block clone_block = (Block) orig2copy[orig_block]; //Console.Out.WriteLine("Fixing branching instructions from " + CodePrinter.b2s(clone_block)); correctBranchVisitor.VisitBlock(clone_block); //Console.Out.WriteLine("Fixing handlers for " + CodePrinter.b2s(clone_block)); FixExceptionalSuccessorForClone(orig_block, clone_block, orig2copy); AddHandlerIfClonedBlockStartsHandler(eh, orig_block, clone_block, orig2copy); // fix_block2next unless it's the last block or a definite branch. Otherwise block2next is not linear if (orig_block != hdlr_last_block) { Block orig_next = (Block) block2next[orig_block]; if (orig_next != null) { Block newNext = Convert(orig_next, orig2copy); if (newNext != orig_next) { block2next[clone_block] = newNext; } } } } // 2nd fixup pass to adjust containing_handler map // this cannot be done in previous loop, since the previous loop // builds the clones of the handlers. // Block finallyStart = eh.HandlerStartBlock; ExceptionHandler origContainingHandlerOfFinally = (ExceptionHandler)this.cfg.b2_containing_handler[finallyStart]; //MayBeNull foreach(Block orig_block in handler_body) { Block clone_block = (Block) orig2copy[orig_block]; ExceptionHandler ceh = (ExceptionHandler) this.cfg.b2_containing_handler[orig_block]; if (ceh != null) { ExceptionHandler eh_clone = (ExceptionHandler) orig2copy[ceh]; if (eh_clone == null) { this.cfg.b2_containing_handler[clone_block] = ceh; } else { this.cfg.b2_containing_handler[clone_block] = eh_clone; } } // the orig_blocks will become part of a new exception handler, so // we also need to redirect their containing_handler to the eh (currently a finally) // that will be turned into an exception handler // We do this by looking up the containing handler of the finally first block, then // changing every block in the finally whose containing handler is the same to the new handler. if (ceh == origContainingHandlerOfFinally) { this.cfg.b2_containing_handler[orig_block] = eh; } } startBlock = (Block) orig2copy[eh.HandlerStartBlock]; lastBlock = (Block) orig2copy[hdlr_last_block]; Debug.Assert(startBlock != null && lastBlock != null); }
public virtual ExceptionHandler VisitExceptionHandler(ExceptionHandler handler) { if (handler == null) return null; handler = (ExceptionHandler)handler.Clone(); handler.BlockAfterHandlerEnd = this.VisitBlock(handler.BlockAfterHandlerEnd); handler.BlockAfterTryEnd = this.VisitBlock(handler.BlockAfterTryEnd); handler.FilterExpression = this.VisitBlock(handler.FilterExpression); handler.FilterType = this.VisitTypeReference(handler.FilterType); handler.HandlerStartBlock = this.VisitBlock(handler.HandlerStartBlock); handler.TryStartBlock = this.VisitBlock(handler.TryStartBlock); return handler; }
private void add_try_start(ExceptionHandler eh, Hashtable/*<Block,Stack<ExceptionHandler>>*/ b2start) { Block eh_start = eh.TryStartBlock; Stack starts = (Stack) b2start[eh_start]; if (starts == null) b2start.Add(eh_start, starts = new Stack()); starts.Push(eh); }
private void add_handler_end(ExceptionHandler eh, Hashtable/*<Block,Queue<ExceptionHandler>>*/ b2end) { Block eh_end = eh.BlockAfterHandlerEnd; Queue ends = (Queue) b2end[eh_end]; if (ends == null) b2end.Add(eh_end, ends = new Queue()); ends.Enqueue(eh); }
/// <summary> /// Computes the ExceptionHandler for each block and the chaining of exception handlers. /// Note: /// Filter handlers are currently treated as starting at the filter expression /// and the endfilter is like a fall through into the actual handler. /// </summary> private bool examine_real_excp_flow(Method method, ExceptionHandlerList ehs, StatementList blocks, out IList/*<ExceptionHandler>*/ all_ehs) { bool has_finally = false; b2exception_handler = new Hashtable(); all_ehs = new ArrayList(); this.handlerThatStartsAtBlock = new Hashtable(); Hashtable/*<Block,Stack<ExceptionHandler>>*/ b2start = new Hashtable(); Hashtable/*<Block,Queue<ExceptionHandler>>*/ b2end = new Hashtable(); Hashtable/*<Block,Queue<ExceptionHandler>>*/ b2handler_end = new Hashtable(); // records for each exception handler, the exception handler tried next e2_next = new Hashtable(); b2_enclosing_finally = new Hashtable(); b2_containing_handler = new Hashtable(); for(int i = 0; i < ehs.Count; i++) { ExceptionHandler eh = ehs[i]; all_ehs.Add(eh); if (eh.HandlerType == NodeType.Finally) has_finally = true; if (eh.HandlerType == NodeType.Filter) { /* * WE NEED TO HANDLE Filter exceptions to do Visual Basic * throw new UnsupportedCodeException(method, "Filter exception handler"); */ // we pretend the filter block is a catch block // for uniform processing while flattening // i.e. it's a block that starts with an implicit // push of the exception, just like a handler. // HACK, we reverse the FilterExpression and HandlerStart so that // We treat the entire filter as a handler Block filter = eh.FilterExpression; eh.FilterExpression = eh.HandlerStartBlock; eh.HandlerStartBlock = filter; } this.handlerThatStartsAtBlock[eh.HandlerStartBlock] = eh; add_try_start(eh, b2start); add_try_end(eh, b2end); add_handler_end(eh, b2handler_end); } FList/*<ExceptionHandler>*/ handlers = FList.Empty; // protecting the current block FList/*<ExceptionHandler>*/ finallies = FList.Empty; // protecting the current block FList/*<ExceptionHandler>*/ containingHandlers = FList.Empty; // containing the current block (top) is current (non-finally) // Push the implicit exceptional exit handler at the bottom of the stack ExceptionHandler handler_around_entire_method = new ExceptionHandler(); handler_around_entire_method.HandlerStartBlock = excp_exit_point; handlers = FList.Cons(handler_around_entire_method, handlers); for(int i = 0; i < blocks.Count; i++) { Block block = (Block) blocks[i]; // pop protecting handlers off stack whose scope ends here Queue ends = (Queue) b2end[block]; if (ends != null) foreach(ExceptionHandler eh in ends) { if (eh == handlers.Head) handlers = handlers.Tail; // Pop handler else throw new ApplicationException("wrong handler"); if (eh.HandlerType == NodeType.Finally) { if (eh == finallies.Head) { finallies = finallies.Tail; // Pop finally } else throw new ApplicationException("wrong finally on stack"); } } // push protecting handlers on stack whose scope starts here Stack starts = (Stack) b2start[block]; if (starts != null) { foreach(ExceptionHandler eh in starts) { ExceptionHandler enclosing = (ExceptionHandler)handlers.Head; // push this handler on top of current block enclosing handlers handlers = FList.Cons(eh, handlers); // Push handler // also record the next enclosing handler for this handler. e2_next[eh.HandlerStartBlock] = enclosing.HandlerStartBlock; // also keep stack of finallies if (eh.HandlerType == NodeType.Finally) { finallies = FList.Cons(eh, finallies); } } } // pop containing handlers off containing stack whose handler scope ends here Queue handler_ends = (Queue) b2handler_end[block]; if (handler_ends != null) { foreach(ExceptionHandler eh in handler_ends) { if (eh == containingHandlers.Head) { containingHandlers = containingHandlers.Tail; // Pop containingHandler } else { throw new ApplicationException("wrong containingHandler on stack"); } } } // push containing handler on stack whose handler scope starts here ExceptionHandler seh = (ExceptionHandler) this.handlerThatStartsAtBlock[block]; if (seh != null) { containingHandlers = FList.Cons(seh, containingHandlers); } // We now add a single exceptional successor, which is the closest enclosing one. ExceptionHandler topeh = (ExceptionHandler)handlers.Head; b2exception_handler.Add(block, topeh.HandlerStartBlock); // We also store the enclosing finally list b2_enclosing_finally.Add(block, finallies); // We also store the map block -> exception handler containing block in handler if (containingHandlers != null) { b2_containing_handler.Add(block, containingHandlers.Head); } } return has_finally; }
// Finds the last block of the ExceptionHandler eh of Method method. private Block LastBlockInsideHandler (ExceptionHandler eh) { if ( ! this.b2index.ContainsKey(eh.BlockAfterHandlerEnd)) { // Apparently, some methods (e.g. System.Management.MTAHelper.WorkerThread // in System.Management.dll) have a handler end block that names // a label (instruction number) beyond the last instruction // (presumably to refer to the end of the method). Naturally, this block // representing this handler end block isn't part of the method body // because it doesn't actually exist. So we artificially add it. method.Body.Statements.Add(eh.BlockAfterHandlerEnd); this.b2index[eh.BlockAfterHandlerEnd] = method.Body.Statements.Count - 1; Debug.Assert(eh.BlockAfterHandlerEnd.Statements.Count == 0); // Can't allow completely empty block. eh.BlockAfterHandlerEnd.Statements.Add(new Return()); } int i = (int) this.b2index[eh.BlockAfterHandlerEnd]; return (Block) method.Body.Statements[i-1]; }
// if a handler starts at orig_block, a cloned handler starts at clone_block. // private void AddHandlerIfClonedBlockStartsHandler ( ExceptionHandler current_eh, Block originalBlock, Block blockCopy, Hashtable/*<Block,Block>*/ orig2copy ) { ExceptionHandler eh = (ExceptionHandler) this.cfg.handlerThatStartsAtBlock[originalBlock]; // The current finally handler is not duplicated (as a handler). It is now part of // the execution path that replaced some leave instruction, not an exception handler. if ((eh == null) || (eh == current_eh)) return; ExceptionHandler clonedHandler = (ExceptionHandler) eh.Clone(); // store the mapping from orig handler to cloned handler in orig2copy (yes, we are overloading this block->block map // with eh -> eh mappings. // We need this so we can adjust the containingHandler of copied blocks. orig2copy[eh] = clonedHandler; if (eh.FilterExpression != null) { clonedHandler.FilterExpression = FindCopyOfBlock(eh.FilterExpression, orig2copy); } clonedHandler.HandlerStartBlock = FindCopyOfBlock(eh.HandlerStartBlock, orig2copy); clonedHandler.TryStartBlock = FindCopyOfBlock(eh.TryStartBlock, orig2copy); /* * The problem with the following code was that the eh.BlockAfter(Handler/Try)End might not have * been cloned, so there no equivalent of it in the cloned world! It would have been better * to identify a handler by its last block, not the first after last ... clonedHandler.BlockAfterHandlerEnd = FindCopyOfBlock(eh.BlockAfterHandlerEnd, orig2copy); clonedHandler.BlockAfterTryEnd = FindCopyOfBlock(eh.BlockAfterTryEnd, orig2copy); if (eh.FilterExpresssion != null) clonedHandler.FilterExpresssion = ImperativeGetCopy(eh.FilterExpresssion, orig2copy); this.b2started_eh[blockCopy] = clonedHandler; */ this.allExceptionHandlers.Add(clonedHandler); this.cfg.handlerThatStartsAtBlock[blockCopy] = clonedHandler; if (eh.HandlerType == NodeType.Finally) { this.lastHandledBlock[clonedHandler] = FindCopyOfBlock((Block) this.lastHandledBlock[eh], orig2copy); } // Sine we're introducing a new handler, we need to update the e2_next // relation, since that determines how handlers are chained together. // Look up the handler invoked after the one being cloned. Block nextHandler = (Block) this.cfg.e2_next[eh.HandlerStartBlock]; if (orig2copy.ContainsKey(nextHandler)) { // If that handler is also being cloned, chain to the // clone rather than the original. nextHandler = (Block) orig2copy[nextHandler]; } this.cfg.e2_next[clonedHandler.HandlerStartBlock] = nextHandler; }
public AvoidClosure (FinallyBlockDuplicator parent, ExceptionHandler eh) { this.parent = parent; this.orig_hdlr_start = (int) parent.b2index[eh.HandlerStartBlock]; this.orig_hdlr_end = (int) parent.b2index[eh.BlockAfterHandlerEnd] - 1; }
// I wish C# had native support for tupples ... /* private class BlockPair { public BlockPair(Block first, Block last) { this.first = first; this.last = last; } public Block first; public Block last; } */ private ISet/*<Block>*/ GetFinallyBody (ExceptionHandler eh, out Block last_block) { StatementList blocks = method.Body.Statements; int hdlr_end_index = (int) b2index[eh.BlockAfterHandlerEnd]; last_block = (Block) blocks[hdlr_end_index-1]; ISet/*<Block>*/ handler_body = GraphUtil.ReachableNodes (DataStructUtil.NodeSetFactory, new Block[]{eh.HandlerStartBlock}, bnav, new DNodePredicate((new AvoidClosure(this, eh)).Avoid)); // ^ isn't it ugly ? what we need are first class functions ... /* Console.Out.WriteLine("handler_body(" + CodePrinter.b2s(eh.HandlerStartBlock) + ") = " + DataStructUtil.IEnum2String(handler_body, CodePrinter.BlockShortPrinter)); */ while ( ! handler_body.Contains(last_block)) { // Crap! The last syntactic block is dead (unreachable), so now // we need a substitute. We search backward until we find one. // This loop is guaranteed to terminate, since we'll eventually // get back to the handler start block, which is trivially reachable // from the handler start block. hdlr_end_index --; last_block = (Block) blocks[hdlr_end_index-1]; } Debug.Assert(handler_body.Contains(last_block), "last block not in blocks reachable from start"); return handler_body; }