예제 #1
0
        // Ensures:
        //  Preconditions != null
        //  && ForAll{Requires r in Preconditions;
        //              r.Condition is BlockExpression
        //              && r.Condition.Type == Void
        //              && IsClump(r.Condition.Block.Statements)
        //  Postconditions != null
        //  && ForAll{Ensures e in Posconditions;
        //              e.PostCondition is BlockExpression
        //              && e.PostCondition.Type == Void
        //              && IsClump(e.PostCondition.Block.Statements)
        //  (In addition, each Requires is a RequiresPlain when the contract
        //   call is Contract.Requires and is a RequiresOtherwise when the
        //   contract call is Critical.Requires. In the latter case, the
        //   ThrowException is filled in correctly.)
        //       
        /// <summary>
        /// 
        /// </summary>
        /// <param name="method"></param>
        /// <param name="Preconditions"></param>
        /// <param name="Postconditions"></param>
        /// <param name="Validations"></param>
        /// <param name="contractInitializerBlock">used to store extra closure initializations from abbrevs and validators</param>
        public void CheapAndDirty(
            Method method,
            ref RequiresList Preconditions,
            ref EnsuresList Postconditions,
            ref RequiresList Validations,
            ref EnsuresList modelPostConditions,
            Block contractInitializerBlock,
            ref HelperMethods.StackDepthTracker dupStackTracker)
        {
            if (this.verbose)
            {
                Console.WriteLine("Method : " + method.FullName);
            }

            if (method == null || method.Body == null || method.Body.Statements == null ||
                method.Body.Statements.Count <= 0)
            {
                return;
            }

            Block methodBody = method.Body;
            int n = methodBody.Statements.Count;
            int beginning = 0;

            while (beginning < n && methodBody.Statements[beginning] is PreambleBlock)
            {
                beginning++;
            }

            int lastBlockContainingContract;
            int lastStatementContainingContract;

            bool anyContractCall = FindLastBlockWithContracts(methodBody.Statements, beginning,
                out lastBlockContainingContract, out lastStatementContainingContract);

            // 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();

            SourceContext lastContractSourceContext = method.SourceContext;
            if (!anyContractCall)
            {
                if (this.verbose)
                {
                    Console.WriteLine("\tNo contracts found");
                }

                // still need to check for bad other contract calls in method body

                goto CheckBody;
            }

            Block lastBlock = methodBody.Statements[lastBlockContainingContract] as Block;
            lastContractSourceContext = lastBlock.SourceContext;

            // probably not a good context, what to do if one can't be found?
            if (lastBlock.Statements != null && 0 <= lastStatementContainingContract &&
                lastStatementContainingContract < lastBlock.Statements.Count)
            {
                lastContractSourceContext = lastBlock.Statements[lastStatementContainingContract].SourceContext;
            }

            // Make sure contract section is not in any try-catch region

            TrivialHashtable<int> block2Index = new TrivialHashtable<int>(methodBody.Statements.Count);
            for (int i = 0, nn = methodBody.Statements.Count; i < nn; i++)
            {
                if (methodBody.Statements[i] == null) continue;
                block2Index[methodBody.Statements[i].UniqueKey] = i;
            }
            // Check each exception handler and see if any overlap with the contract section
            for (int i = 0, nn = method.ExceptionHandlers == null ? 0 : method.ExceptionHandlers.Count; i < nn; i++)
            {
                ExceptionHandler eh = method.ExceptionHandlers[i];
                if (eh == null) continue;

                if (((int) block2Index[eh.BlockAfterTryEnd.UniqueKey]) < beginning ||
                    lastBlockContainingContract < ((int) block2Index[eh.TryStartBlock.UniqueKey]))
                {
                    continue; // can't overlap
                }

                this.HandleError(method, 1024, "Contract section within try block.", lastContractSourceContext);
                return;
            }

            // Extract <beginning,0> to <lastBlockContainingContract,lastStatmentContainingContract>
            StatementList contractClump = HelperMethods.ExtractClump(methodBody.Statements, beginning, 0,
                lastBlockContainingContract, lastStatementContainingContract);

            // Look for bad stuff

            BadStuff(method, contractClump, lastContractSourceContext);

            // Make sure that the entire contract section is closed.
            if (!CheckClump(method, gatherLocals, currentMethodSourceContext, new Block(contractClump)))
            {
                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();

            Preconditions = new RequiresList();
            Postconditions = new EnsuresList();
            Validations = new RequiresList();
            modelPostConditions = new EnsuresList();

            if (!ExtractFromClump(
                contractClump, method, gatherLocals, Preconditions, Postconditions, Validations,
                modelPostConditions, lastContractSourceContext, method, contractInitializerBlock, ref dupStackTracker))
            {
                return;
            }

            CheckBody:

            // Check "real" method body for use of any locals used in contracts

            //      var checkMethodBody = new CheckForBadContractStuffInMethodBody(gatherLocals, this.CallErrorFound, method);
            var checkMethodBody = new CheckLocals(gatherLocals);
            checkMethodBody.Visit(methodBody);

            if (!this.fSharp && checkMethodBody.reUseOfExistingLocal != null)
            {
                SourceContext sc = lastContractSourceContext;

                this.HandleError(method, 1025,
                    "After contract block, found use of local variable '" +
                    checkMethodBody.reUseOfExistingLocal.Name.Name + "' defined in contract block", sc);
            }
        }
예제 #2
0
        private bool CheckClump(Method method, GatherLocals gatherLocals, SourceContext sctx, Block clump)
        {
            if (!HelperMethods.ClumpIsClosed(clump.Statements))
            {
                this.contractNodes.CallErrorFound(new Error(1017,
                    "Malformed contract section in method '" + method.FullName + "'", sctx));
                return false;
                //            throw new ExtractorException();
            }

            TypeNode t = method.DeclaringType;
            bool IsContractTypeForSomeOtherType = t == null
                ? false
                : HelperMethods.GetTypeFromAttribute(t, ContractNodes.ContractClassForAttributeName) != null;

            // We require that a contract class implement the interface it is holding contracts for. In addition, the methods in that class must be explicit
            // interface implementations. This creates a problem when the contracts need to refer to other methods in the interface. The "this" reference has
            // the wrong type: it needs to be the interface type. Writing an explicit cast before every reference is painful. It is better to allow the user
            // to create a local variable of the interface type and assign it before the contracts. This can't cause a problem with the local being used later
            // in the method body, because methods in contract classes don't have method bodies, just contracts. So don't check for re-use of locals for such
            // methods.
            //

            // Example of above comment

            //[ContractClass(typeof(ContractForJ))]
            //interface J{
            //  bool M(int x);
            //  [Pure]
            //  bool P { get; }
            //}

            //[ContractClassFor(typeof(J))]
            //class ContractForJ : J{
            //  bool J.M(int x) {
            //    J jThis = this;
            //    Contract.Requires(x != 3);
            //    Contract.Requires(jThis.P);
            //    Contract.Ensures(x != 5 || !jThis.P);
            //    return default(bool);
            //  }
            //  bool J.P {
            //    get {
            //      Contract.Ensures(Contract.Result<bool>());
            //      return default(bool);
            //    }
            //  }
            //}

            if (!IsContractTypeForSomeOtherType)
            {
                // First, make sure the clump doesn't use any locals that were used
                // in any previous contract, need a new instance each time
                CheckLocals checkLocals = new CheckLocals(gatherLocals);
                checkLocals.Visit(clump);

                if (!this.fSharp && checkLocals.reUseOfExistingLocal != null)
                {
                    this.HandleError(method, 1040,
                        "Reuse of existing local variable '" + checkLocals.reUseOfExistingLocal.Name.Name +
                        "' in contract.", sctx);
                    return false;
                }

                // If that test passes, then add in the locals used in the clump into the table of locals that have been used
                gatherLocals.Visit(clump);
            }

            return true;
        }