private void ProcessClosureClass(Method method, TypeNode closure, bool isAsync) { Contract.Requires(method != null); Contract.Requires(closure != null); Method movenext = closure.GetMethod(StandardIds.MoveNext); if (movenext == null) return; movenext.IsAsync = isAsync; if (movenext.Body == null) return; if (movenext.Body.Statements == null) return; SourceContext defaultSourceContext; Block contractInitializerBlock = new Block(new StatementList()); HelperMethods.StackDepthTracker dupStackTracker = new HelperMethods.StackDepthTracker(); AssumeBlock originalContractPosition; StatementList contractClump = GetContractClumpFromMoveNext(method, movenext, contractNodes, contractInitializerBlock.Statements, out defaultSourceContext, ref dupStackTracker, out originalContractPosition); if (contractClump != null) { // Look for bad stuff BadStuff(method, contractClump, defaultSourceContext); // Make sure any locals in the contracts are disjoint from the locals in the rest of the body // can use the same one throughout GatherLocals gatherLocals = new GatherLocals(); // Make sure that the entire contract section is closed. if (!CheckClump(movenext, gatherLocals, currentMethodSourceContext, new Block(contractClump))) { movenext.ClearBody(); return; } // Checking that had the side effect of populating the hashtable, but now each contract will be individually visited. // That process needs to start with a fresh table. gatherLocals.Locals = new TrivialHashtable(); RequiresList Preconditions = new RequiresList(); EnsuresList Postconditions = new EnsuresList(); RequiresList Validations = new RequiresList(); EnsuresList modelPostconditions = new EnsuresList(); EnsuresList asyncPostconditions = null; // REVIEW: What should we do with the Validations in this case? Should we map them to the enumerator method? Maybe not, since without // rewriting this won't happen. if (!ExtractFromClump(contractClump, movenext, gatherLocals, Preconditions, Postconditions, Validations, modelPostconditions, defaultSourceContext, method, contractInitializerBlock, ref dupStackTracker)) { movenext.ClearBody(); return; } if (isAsync) { asyncPostconditions = SplitAsyncEnsures(ref Postconditions, method); } try { // Next is to attach the preconditions to method (instead of movenext) // To do so, we have to duplicate the expressions and statements in Precondition, Postcondition and contractInitializerBlock Duplicator dup = new Duplicator(closure.DeclaringModule, method.DeclaringType); var origPreconditions = Preconditions; var origValidations = Validations; var origcontractInitializerBlock = contractInitializerBlock; Preconditions = dup.VisitRequiresList(Preconditions); Postconditions = dup.VisitEnsuresList(Postconditions); Validations = dup.VisitRequiresList(Validations); contractInitializerBlock = dup.VisitBlock(contractInitializerBlock); asyncPostconditions = dup.VisitEnsuresList(asyncPostconditions); var mapClosureExpToOriginal = BuildMappingFromClosureToOriginal(closure, movenext, method); Preconditions = mapClosureExpToOriginal.Apply(Preconditions); Postconditions = mapClosureExpToOriginal.Apply(Postconditions); Validations = mapClosureExpToOriginal.Apply(Validations); contractInitializerBlock = mapClosureExpToOriginal.Apply(contractInitializerBlock); asyncPostconditions = mapClosureExpToOriginal.Apply(asyncPostconditions); //MemberList members = FindClosureMembersInContract(closure, movenext); // MakeClosureAccessibleToOriginalMethod(closure, members); if (method.Contract == null) method.Contract = new MethodContract(method); method.Contract.Requires = Preconditions; method.Contract.Validations = Validations; // Postconditions are sanity checked here, because Result<T> must be compared against the // return type of the original method. It is most conveniently done after the type substitution. // TODO: refactor the checking part altogether out of ExtractFromClump. method.Contract.Ensures = Postconditions; method.Contract.ModelEnsures = modelPostconditions; method.Contract.ContractInitializer = contractInitializerBlock; method.Contract.AsyncEnsures = asyncPostconditions; // Following replacement causes some weird issues for complex preconditions (like x != null && x.Length > 0) // when CCRewriter is used with /publicsurface or Preconditions only. // This fix could be temporal and proper fix would be applied in the future. // After discussion this issue with original CC authors (Mike Barnett and Francesco Logozzo), // we decided that this fix is safe and lack of Assume statement in the MoveNext method will not affect // customers (neither CCRewriter customers nor CCCheck customers). // If this assumption would not be true in the future, proper fix should be applied. // put requires as assumes into movenext method at original position // ReplaceRequiresWithAssumeInMoveNext(origPreconditions, originalContractPosition); // no postPreamble to initialize, as method is not a ctor } finally { // this is done in caller!!! //// normalize contract by forcing IsPure to look at attributes and removing contract it is empty //var contract = method.Contract; //var isPure = contract.IsPure; //if (!isPure && contract.RequiresCount == 0 && contract.EnsuresCount == 0 && contract.ModelEnsuresCount == 0 && contract.ValidationsCount == 0 && contract.AsyncEnsuresCount == 0) //{ // method.Contract = null; //} else //{ // // turn helper method calls to Result, OldValue, ValueAtReturn into proper AST nodes. // this.extractionFinalizer.VisitMethodContract(method.Contract); //} } } }