private void RecordEnsures( Method method, ref int oldLocalUniqueNameCounter, List<Block> contractInitializationBlocks, Dictionary<TypeNode, Local> closureLocals, Block oldExpressionPreStateValues, List<Ensures> postconditions, List<Ensures> asyncPostconditions, WrapParametersInOldExpressions wrap, ref EmitAsyncClosure asyncBuilder, MethodContract mc) { RewriteHelper.RecordClosureInitialization(method, mc.ContractInitializer, closureLocals); if (this.Emit(RuntimeContractEmitFlags.Ensures) && mc.Ensures != null) { mc.Ensures = wrap.VisitEnsuresList(mc.Ensures); Block oldInits = ProcessOldExpressions(method, mc.Ensures, closureLocals, ref oldLocalUniqueNameCounter); if (oldInits != null) { oldExpressionPreStateValues.Statements.Add(oldInits); } if (mc.Ensures != null) { foreach (Ensures ensures in mc.Ensures) { if (!EmitEnsures(ensures, method.DeclaringType, this.skipQuantifiers)) continue; postconditions.Add(ensures); } } } if (this.Emit(RuntimeContractEmitFlags.AsyncEnsures) && mc.AsyncEnsuresCount > 0) { mc.AsyncEnsures = wrap.VisitEnsuresList(mc.AsyncEnsures); var found = false; foreach (Ensures postcondition in mc.AsyncEnsures) { if (!EmitEnsures(postcondition, method.DeclaringType, this.skipQuantifiers) || asyncPostconditions.Contains(postcondition)) continue; EnsureAsyncBuilder(method, contractInitializationBlocks, closureLocals, ref asyncBuilder); found = true; asyncPostconditions.Add(postcondition); } if (found) { Block oldInit = ProcessOldExpressionsInAsync(method, mc.AsyncEnsures, closureLocals, ref oldLocalUniqueNameCounter, asyncBuilder.ClosureClass); if (oldInit != null && oldInit.Statements != null && oldInit.Statements.Count > 0) { oldExpressionPreStateValues.Statements.Add(oldInit); } } } }
//[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 }