Пример #1
0
        private void ExtractContractsForMethod(Method method, object dummy)
        {
            Contract.Requires(method != null);

            RequiresList preconditions = null;
            EnsuresList postconditions = null;
            RequiresList validations = null;
            EnsuresList modelPostconditions = null;

            //Console.WriteLine("Extracting contract for method {0}", method.FullName);

            // set its contract so pure is properly handled.
            var methodContract = method.Contract = new MethodContract(method);

            try
            {
                if (method.IsAbstract /* && contractClass == null */)
                {
                    // Abstract methods cannot have a body, so nothing to extract
                    return;
                }

                TypeNode /*?*/ closure = null;
                bool possiblyAsync;
                if (IsIteratorOrAsyncMethodCandidate(method, out possiblyAsync))
                {
                    closure = FindClosureClass(method);
                    if (closure != null)
                    {
                        this.ProcessClosureClass(method, closure, possiblyAsync);
                        return;
                    }
                }

                if (method.Body == null || method.Body.Statements == null)
                    return;

                // Find first source context, use it if no better one is found on errors

                // if (method.Body != null && method.Body.Statements != null)
                {
                    if (this.verbose)
                    {
                        Console.WriteLine(method.FullName);
                    }

                    bool found = false;
                    for (int i = 0, n = method.Body.Statements.Count; i < n && !found; i++)
                    {
                        Block b = method.Body.Statements[i] as Block;
                        if (b != null)
                        {
                            for (int j2 = 0, m = b.Statements == null ? 0 : b.Statements.Count; j2 < m; j2++)
                            {
                                Contract.Assert(m == b.Statements.Count, "loop invariant not inferred");

                                Statement s = b.Statements[j2];
                                if (s != null)
                                {
                                    SourceContext sctx = s.SourceContext;
                                    if (sctx.IsValid)
                                    {
                                        found = true;
                                        // s.SourceContext = new SourceContext(); // wipe out the source context because this statement will no longer be the first one in the method body if there are any contract calls
                                        this.currentMethodSourceContext = sctx;
                                        if (this.verbose)
                                        {
                                            Console.WriteLine("block {0}, statement {1}: ({2},{3})", i, j2, sctx.StartLine, sctx.StartColumn);
                                        }

                                        break;
                                    }
                                }
                            }
                        }
                    }
                }

                Block contractInitializerBlock = new Block(new StatementList());
                Block postPreamble = null;

                HelperMethods.StackDepthTracker dupStackTracker = new HelperMethods.StackDepthTracker();

                var contractLocalAliasingThis = HelperMethods.ExtractPreamble(method, this.contractNodes,
                    contractInitializerBlock, out postPreamble, ref dupStackTracker, this.isVB);

                bool saveErrorFound = this.errorFound;
                this.errorFound = false;

                // Extract pre- and postconditions

                if (method.Body != null && method.Body.Statements != null)
                {
                    this.CheapAndDirty(method, ref preconditions, ref postconditions, ref validations,
                        ref modelPostconditions, contractInitializerBlock, ref dupStackTracker);

                    if (this.errorFound)
                    {
                        method.ClearBody();

                        this.errorFound = saveErrorFound;
                        return;
                    }

                    this.errorFound = saveErrorFound;
                }

                // Sanitize contract by renaming local aliasing "this" to This

                if (contractLocalAliasingThis != null && method.ThisParameter != null)
                {
                    var renamer = new ContractLocalToThis(contractLocalAliasingThis, method.ThisParameter);
                    renamer.Visit(preconditions);
                    renamer.Visit(postconditions);
                    renamer.Visit(modelPostconditions);
                }

                // Split out async ensures

                var asyncPostconditions = SplitAsyncEnsures(ref postconditions, method);

                // Store contracts into the appropriate slots

                Contract.Assume(methodContract.RequiresCount == 0);
                Contract.Assume(methodContract.EnsuresCount == 0);
                Contract.Assume(methodContract.ModelEnsuresCount == 0);

                methodContract.ContractInitializer = contractInitializerBlock;
                methodContract.PostPreamble = postPreamble;
                methodContract.Requires = preconditions;
                methodContract.Ensures = postconditions;
                methodContract.AsyncEnsures = asyncPostconditions;
                methodContract.ModelEnsures = modelPostconditions;
                methodContract.Validations = validations;

                return;
            }
            catch (NotImplementedException ni)
            {
                // indicates a problem
                this.HandleError(method, 1099, "Contract extraction failed: " + ni.Message, default(SourceContext));
            }
            finally
            {
                // normalize contract by forcing IsPure to look at attributes and removing contract it is empty
                var isPure = methodContract.IsPure;

                if (!isPure && methodContract.RequiresCount == 0 &&
                    methodContract.EnsuresCount == 0 &&
                    methodContract.ModelEnsuresCount == 0 &&
                    methodContract.AsyncEnsuresCount == 0 &&
                    methodContract.ValidationsCount == 0)
                {
                    method.Contract = null;
                }
                else
                {
                    // turn helper method calls to Result, OldValue, ValueAtReturn into proper AST nodes.
                    this.extractionFinalizer.VisitMethodContract(methodContract);
                }
            }
        }
Пример #2
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);
                    //}
                }
            }
        }