private bool ExtractFromClump(StatementList contractClump, Method method, GatherLocals gatherLocals, RequiresList Preconditions, EnsuresList Postconditions, RequiresList validations, EnsuresList modelPostconditions, SourceContext defaultContext, Method originalMethod, Block contractInitializer, ref HelperMethods.StackDepthTracker dupStackTracker) { // set the state so that the contract clump is used for extraction (as opposed to the method body as it used to) StatementList stmts = contractClump; int beginning = 0; int n = stmts.Count; int seginning = HelperMethods.FindNextRealStatement(((Block) stmts[beginning]).Statements, 0); bool endContractFound = false; bool postConditionFound = false; SourceContext currentSourceContext; for (int i = beginning; i < n; i++) { Block b = (Block) stmts[i]; if (b == null) continue; for (int j = 0, m = b.Statements == null ? 0 : b.Statements.Count; j < m; j++) { if (dupStackTracker.IsValid && dupStackTracker.Depth >= 0) { b.Statements[j] = dupStackTracker.Visit(b.Statements[j]); } Statement s = b.Statements[j]; if (s == null) continue; Block currentClump; Throw t = null; t = s as Throw; Method calledMethod = HelperMethods.IsMethodCall(s); if ((t != null || (calledMethod != null && calledMethod.DeclaringType != null && calledMethod.DeclaringType != this.contractNodes.ContractClass && HelperMethods.IsVoidType(calledMethod.ReturnType) && !this.contractNodes.IsContractOrValidatorOrAbbreviatorMethod(calledMethod)))) { // Treat throw statements as (part of) a precondition // don't accept "throw ..." unless it comes in the "precondition section" // then treat the current clump as a precondition, but need to massage it a bit: // all branches to the block just after the throw should be modified to be branches to // a new manufactured block that sets a fresh local to "true". The // throw itself should be changed to set the same local to "false". That way the // clump can be treated as the value of precondition (because the branch polarity has // already been negated as part of the code gen). // This test was supposed to be a sanity check that the current block contained // only "throw ..." or else "nop; throw ...". But I've also seen "ThrowHelper.Throw(...); nop", // so I'm just going to comment this out for now. //if (!((m == 1 && j == 0) || (m == 2 && j == 1))) { // Preconditions = new RequiresList(); // Postconditions = new EnsuresList(); // return; // throw new ExtractorException(); //} Expression exception; // The clump being extracted may contain code/blocks that represent (part of) // the expression that is being thrown (if the original throw expression had // control flow in it from boolean expressions and/or ternary expressions). b.Statements[j] = null; // wipe out throw statement currentClump = new Block(HelperMethods.ExtractClump(stmts, beginning, seginning, i, j)); int currentClumpLength = i - beginning + 1; // there better be a next block because that must have been the target for all of the branches // that didn't cause the throw to happen if (!(i < n - 1)) { this.HandleError(method, 1027, "Malformed contract.", s.SourceContext); return false; } Block nextBlock = (Block) stmts[i + 1]; // cast succeeds because body is clump Local valueOfPrecondition = new Local(Identifier.For("_preConditionHolds"), SystemTypes.Boolean); Block preconditionHolds = new Block(new StatementList(new AssignmentStatement(valueOfPrecondition, Literal.True))); ReplaceBranchTarget rbt = new ReplaceBranchTarget(nextBlock, preconditionHolds); rbt.VisitBlock(currentClump); int ILOffset; CountPopExpressions cpe = new CountPopExpressions(); currentSourceContext = s.SourceContext; cpe.Visit(s); if (0 < cpe.PopOccurrences) { // then there is a set of blocks that represent the exception: the Reader // was not able to decompile it back into an expression. Extract the set // from the current clump and make it into a block expression // Find the last block that has a branch to "preconditionHolds". After that are all of the blocks // that represent the evaluation of the exception int branchBlockIndex = currentClumpLength - 2; // can't be the current block: that has the throw in it while (0 <= branchBlockIndex) { Block possibleBranchBlock = currentClump.Statements[branchBlockIndex] as Block; Branch br = possibleBranchBlock.Statements[possibleBranchBlock.Statements.Count - 1] as Branch; if (br != null && br.Target == preconditionHolds) { break; } branchBlockIndex--; } if (branchBlockIndex < 0) { this.HandleError(method, 1028, "Malformed exception constructor in contract.", defaultContext); return false; } Block exceptionBlock = new Block(HelperMethods.ExtractClump(currentClump.Statements, branchBlockIndex + 1, 0, currentClumpLength - 1, ((Block) currentClump.Statements[currentClumpLength - 1]).Statements.Count - 1)); exceptionBlock.Statements.Add(new ExpressionStatement(t.Expression)); SourceContext sctx = ((Block) exceptionBlock.Statements[0]).Statements[0].SourceContext; if (sctx.IsValid) { currentSourceContext = sctx; } else { SourceContext tmp; bool foundContext = HelperMethods.GetLastSourceContext(exceptionBlock.Statements, out tmp); if (foundContext) currentSourceContext = tmp; } if (!CheckClump(method, gatherLocals, currentSourceContext, exceptionBlock)) return false; exception = new BlockExpression(exceptionBlock, SystemTypes.Exception); ILOffset = t.ILOffset; } else { currentSourceContext = s.SourceContext; if (t != null) { // then the statement is "throw ..." exception = t.Expression; ILOffset = t.ILOffset; } else { ExpressionStatement throwHelperCall = s as ExpressionStatement; Debug.Assert(throwHelperCall != null); exception = throwHelperCall.Expression; ILOffset = s.ILOffset; } exception.SourceContext = currentSourceContext; SourceContext tmp; bool foundContext = HelperMethods.GetLastSourceContext(currentClump.Statements, out tmp); if (foundContext) currentSourceContext = tmp; } Block returnValueOfPrecondition = new Block(new StatementList(new ExpressionStatement(valueOfPrecondition))); Statement extraAssumeFalse = this.ExtraAssumeFalseOnThrow(); Block preconditionFails = new Block(new StatementList(new AssignmentStatement(valueOfPrecondition, Literal.False), extraAssumeFalse, new Branch(null, returnValueOfPrecondition, true, false, false))); //Block preconditionFails = new Block(new StatementList(new AssignmentStatement(valueOfPrecondition, Literal.False), new Branch(null, returnValueOfPrecondition, true, false, false))); currentClump.Statements.Add(preconditionFails); // replace throw statement currentClump.Statements.Add(preconditionHolds); currentClump.Statements.Add(returnValueOfPrecondition); if (!CheckClump(originalMethod, gatherLocals, currentSourceContext, currentClump)) return false; BlockExpression be = new BlockExpression(currentClump, SystemTypes.Boolean); be.SourceContext = currentSourceContext; var ro = new RequiresOtherwise(be, exception); ro.ILOffset = ILOffset; ro.SourceContext = currentSourceContext; if (postConditionFound) { HandleError(originalMethod, 1013, "Precondition found after postcondition.", currentSourceContext); return false; } validations.Add(ro); var req = new RequiresPlain(be, FindExceptionThrown.Find(exception)); req.IsFromValidation = true; req.ILOffset = ro.ILOffset; req.SourceContext = ro.SourceContext; Preconditions.Add(req); } else { if (contractNodes.IsContractMethod(calledMethod)) { // Treat calls to contract methods if (endContractFound) { HandleError(originalMethod, 1012, "Contract call found after prior EndContractBlock.", s.SourceContext); break; } if (contractNodes.IsEndContract(calledMethod)) { endContractFound = true; continue; } MethodCall mc = ((ExpressionStatement) s).Expression as MethodCall; Expression arg = mc.Operands[0]; arg.SourceContext = s.SourceContext; MethodContractElement mce; currentSourceContext = s.SourceContext; Expression condition; if (beginning == i && seginning == j) { // Deal with the simple case: the reader decompiled the call into a single statement condition = arg; } else { b.Statements[j] = new ExpressionStatement(arg); // construct a clump from // methodBody.Statements[beginning].Statements[seginning] to // methodBody.Statements[i].Statements[j] currentClump = new Block(HelperMethods.ExtractClump(stmts, beginning, seginning, i, j)); if (!currentSourceContext.IsValid) { // then a good source context has not been found yet. Grovel around in the clump // to see if there is a better one SourceContext sctx; if (HelperMethods.FindContext(currentClump, currentSourceContext, out sctx)) currentSourceContext = sctx; } if (!CheckClump(originalMethod, gatherLocals, currentSourceContext, currentClump)) return false; BlockExpression be = new BlockExpression(currentClump); condition = be; } condition.SourceContext = currentSourceContext; if (contractNodes.IsPlainPrecondition(calledMethod)) { var req = new RequiresPlain(condition); contractNodes.IsRequiresWithException(calledMethod, out req.ExceptionType); mce = req; } else if (this.contractNodes.IsPostcondition(calledMethod)) { mce = new EnsuresNormal(condition); } else if (contractNodes.IsExceptionalPostcondition(calledMethod)) { EnsuresExceptional ee = new EnsuresExceptional(condition); // Extract the type of exception. ee.Type = calledMethod.TemplateArguments[0]; mce = ee; } else { throw new InvalidOperationException("Cannot recognize contract method"); } mce.SourceContext = currentSourceContext; mce.ILOffset = mc.ILOffset; if (1 < mc.Operands.Count) { var candidate = SanitizeUserMessage(method, mc.Operands[1], currentSourceContext); mce.UserMessage = candidate; } if (2 < mc.Operands.Count) { Literal lit = mc.Operands[2] as Literal; if (lit != null) { mce.SourceConditionText = lit; } } // determine Model status mce.UsesModels = CodeInspector.UsesModel(mce.Assertion, this.contractNodes); // Check context rules switch (mce.NodeType) { case NodeType.RequiresPlain: if (postConditionFound) { this.HandleError(originalMethod, 1014, "Precondition found after postcondition.", currentSourceContext); return false; } if (mce.UsesModels) { this.HandleError(originalMethod, 1073, "Preconditions may not refer to model members.", currentSourceContext); return false; } var rp = (RequiresPlain) mce; Preconditions.Add(rp); validations.Add(rp); // also add to the internal validation list break; // TODO: check visibility of post conditions based on visibility of possible implementation case NodeType.EnsuresNormal: case NodeType.EnsuresExceptional: Ensures ensures = (Ensures) mce; if (mce.UsesModels) { if (this.IncludeModels) { modelPostconditions.Add(ensures); } } else { Postconditions.Add(ensures); } postConditionFound = true; break; } } else if (ContractNodes.IsValidatorMethod(calledMethod)) { // Treat calls to Contract validators if (endContractFound) { this.HandleError(originalMethod, 1012, "Contract call found after prior EndContractBlock.", s.SourceContext); break; } MethodCall mc = ((ExpressionStatement) s).Expression as MethodCall; var memberBinding = (MemberBinding) mc.Callee; currentSourceContext = s.SourceContext; Statement validation; Block validationPrefix; if (beginning == i && seginning == j) { // Deal with the simple case: the reader decompiled the call into a single statement validation = s; validationPrefix = null; } else { // The clump may contain multiple statements ending in the validator call. // to extract the code as Requires<E>, we need to keep the statements preceeding // the validator call, as they may contain local initialization etc. These should go // into the first Requires<E> that the validator expands to. This way, if there are // no Requires<E> expanded from the validator, then the statements can be omitted. // At the same time, the statements won't be duplicated when validations are emitted. // // If the validator call contains any pops, then the extraction must fail saying it // is too complicated. // must null out statement with call before extract clump b.Statements[j] = null; // we have a copy in mc, s validationPrefix = new Block(HelperMethods.ExtractClump(stmts, beginning, seginning, i, j)); if (!currentSourceContext.IsValid) { // then a good source context has not been found yet. Grovel around in the clump // to see if there is a better one SourceContext sctx; if (HelperMethods.FindContext(validationPrefix, currentSourceContext, out sctx)) currentSourceContext = sctx; } if (CountPopExpressions.Count(mc) > 0) { this.HandleError(method, 1071, "Arguments to contract validator call are too complicated. Please simplify.", currentSourceContext); return false; } if (!CheckClump(originalMethod, gatherLocals, currentSourceContext, validationPrefix)) return false; validation = new Block(new StatementList(validationPrefix, s)); validation.SourceContext = currentSourceContext; } var ro = new RequiresOtherwise(null, new BlockExpression(new Block(new StatementList(validation)))); validations.Add(ro); CopyValidatorContracts( method, calledMethod, memberBinding.TargetObject, mc.Operands, Preconditions, currentSourceContext, validationPrefix); } else if (ContractNodes.IsAbbreviatorMethod(calledMethod)) { // Treat calls to Contract abbreviators if (endContractFound) { this.HandleError(originalMethod, 1012, "Contract call found after prior EndContractBlock.", s.SourceContext); break; } MethodCall mc = ((ExpressionStatement) s).Expression as MethodCall; var memberBinding = (MemberBinding) mc.Callee; currentSourceContext = s.SourceContext; if (beginning == i && seginning == j) { // Deal with the simple case: the reader decompiled the call into a single statement // nothing to do. All is in the call and its arguments } else { // The clump may contain multiple statements ending in the abbreviator call. // We need to keep the statements preceeding the abbreviator call and add them to the // contract initializer block. The reason we cannot add them to the first expansion contract // of the abbreviator is that the abbreviator may give rise to closure initialization which will // be hoisted into the closure initializer block. This closure initializer may refer to the // locals initialized by the present statement sequence, so it must precede it. // // If the abbreviator call contains any pops, then the extraction must fail saying it // is too complicated. // grab prefix of clump minus last call statement. // must null out current call statement before we extract clump (ow. it stays in body) b.Statements[j] = null; currentClump = new Block(HelperMethods.ExtractClump(stmts, beginning, seginning, i, j)); if (!currentSourceContext.IsValid) { // then a good source context has not been found yet. Grovel around in the clump // to see if there is a better one SourceContext sctx; if (HelperMethods.FindContext(currentClump, currentSourceContext, out sctx)) currentSourceContext = sctx; } if (CountPopExpressions.Count(mc) > 0) { this.HandleError(method, 1070, "Arguments to contract abbreviator call are too complicated. Please simplify.", currentSourceContext); return false; } if (!CheckClump(originalMethod, gatherLocals, currentSourceContext, currentClump)) return false; if (HelperMethods.IsNonTrivial(currentClump)) { contractInitializer.Statements.Add(currentClump); } } CopyAbbreviatorContracts(method, calledMethod, memberBinding.TargetObject, mc.Operands, Preconditions, Postconditions, currentSourceContext, validations, contractInitializer); } else { // important to continue here and accumulate blocks/statements for next contract! if (i == beginning && j == seginning && s.NodeType == NodeType.Nop) { // nop following contract is often associated with previous code, so skip it seginning = j + 1; } continue; } } // Re-initialize current state after contract has been found beginning = i; seginning = j + 1; //seginning = HelperMethods.FindNextRealStatement(((Block)stmts[i]).Statements, j + 1); if (seginning < 0) seginning = 0; //b = (Block)stmts[i]; // IMPORTANT! Need this to keep "b" in sync } } if (this.verbose) { Console.WriteLine("\tNumber of Preconditions: " + Preconditions.Count); Console.WriteLine("\tNumber of Postconditions: " + Postconditions.Count); } return true; }
// 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); } }
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); //} } } }
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; }
public CheckLocals(GatherLocals gatherLocals) { this.gatherLocals = gatherLocals; }
public CheckLocals(GatherLocals gatherLocals) { this.gatherLocals = gatherLocals; }