Esempio n. 1
0
        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);
                    //}
                }
            }
        }