private StatementList GetContractClumpFromMoveNext(Method iteratorMethod, Method moveNext, ContractNodes contractNodes, StatementList contractInitializer, out SourceContext defaultSourceContext, ref HelperMethods.StackDepthTracker dupStackTracker, out AssumeBlock originalContractPosition) { Contract.Requires(moveNext != null); Contract.Requires(moveNext.Body != null); Contract.Requires(moveNext.Body.Statements != null); Contract.Requires(contractInitializer != null); Contract.Requires(iteratorMethod != null); var linkerVersion = 0; if (iteratorMethod.DeclaringType != null && iteratorMethod.DeclaringType.DeclaringModule != null) { linkerVersion = iteratorMethod.DeclaringType.DeclaringModule.LinkerMajorVersion; } var initialState = moveNext.IsAsync ? -1 : 0; moveNext.MoveNextStartState = initialState; originalContractPosition = null; int statementIndex; Contract.Assume(moveNext.Body != null); Contract.Assume(moveNext.Body.Statements != null); int blockIndex = ContractStartInMoveNext(this.contractNodes, moveNext, out statementIndex, iteratorMethod); Contract.Assert(statementIndex >= 0, "should follow from the postcondiiton"); if (blockIndex < 0) { // Couldn't find state 0 in MoveNext method // This can happen if the iterator is trivial (like yield break; ) defaultSourceContext = default(SourceContext); return null; } int beginning = blockIndex; // the block number in the body of movenext int sbeginning = statementIndex; // the statement no. in the beginnning block after the preamble int blast = -1; // the block number in the body of movenext, of the last block where there is a contract call int slast = -1; // the statement no. in the blast block // Next we move sbeginning past the preamble area sbeginning = MovePastPreamble(iteratorMethod, moveNext, beginning, sbeginning, contractInitializer, contractNodes, ref dupStackTracker); Contract.Assert(moveNext.Body != null, "should be provable"); Contract.Assert(moveNext.Body.Statements != null, "should be provable"); if (sbeginning < 0 || !this.FindLastBlockWithContracts(moveNext.Body.Statements, beginning, out blast, out slast)) { if (verbose) { if (moveNext.Name != null) { Console.WriteLine("Method {0} doesnt have a contract method invocation at the right place.", moveNext.Name.Name); } } defaultSourceContext = default(SourceContext); return null; } Block methodBody = moveNext.Body; Block lastBlock = methodBody.Statements[blast] as Block; SourceContext lastContractSourceContext; if (lastBlock != null) { lastContractSourceContext = lastBlock.SourceContext; // probably not a good context, what to do if one can't be found? if (lastBlock.Statements != null && 0 <= slast && slast < lastBlock.Statements.Count) { if (lastBlock.Statements[slast] != null) { lastContractSourceContext = lastBlock.Statements[slast].SourceContext; } } } else { lastContractSourceContext = default(SourceContext); } // TODO: check the clump is not in a try-catch block. originalContractPosition = new AssumeBlock(new StatementList()); StatementList result = HelperMethods.ExtractClump( moveNext.Body.Statements, beginning, sbeginning, blast, slast, assumeBlock: originalContractPosition); defaultSourceContext = lastContractSourceContext; return result; }
internal static StatementList ExtractClump(StatementList blocks, int firstBlockIndex, int firstStmtIndex, int lastBlockIndex, int lastStmtIndex, AssumeBlock assumeBlock = null) { Contract.Requires(blocks != null); Contract.Requires(firstBlockIndex >= 0); Contract.Requires(firstStmtIndex >= 0); Contract.Ensures(Contract.Result<StatementList>().Count == lastBlockIndex - firstBlockIndex + 1); Contract.Ensures(blocks[firstBlockIndex] == Contract.OldValue(blocks[firstBlockIndex])); StatementList clump = new StatementList(); // first extract the tail of the first block into a new block. Block oldFirstBlock = (Block) blocks[firstBlockIndex]; Block newFirstBlock = new Block(new StatementList()); if (oldFirstBlock != null) { var count = firstBlockIndex == lastBlockIndex ? lastStmtIndex + 1 : oldFirstBlock.Statements.Count; for (int stmtIndex = firstStmtIndex; stmtIndex < count; stmtIndex++) { var stmt = oldFirstBlock.Statements[stmtIndex]; newFirstBlock.Statements.Add(stmt); if (stmt == null) continue; oldFirstBlock.Statements[stmtIndex] = assumeBlock; assumeBlock = null; } } clump.Add(newFirstBlock); var currentBlockIndex = firstBlockIndex + 1; if (currentBlockIndex > lastBlockIndex) return clump; // setup info about forwarding branches to new last full block Block newLastBlock = null; var lastFullBlock = lastBlockIndex - 1; var oldLastBlock = (Block) blocks[lastBlockIndex]; if (oldLastBlock != null && lastStmtIndex == oldLastBlock.Statements.Count - 1) { // last block is also fully used. lastFullBlock = lastBlockIndex; } else { newLastBlock = new Block(new StatementList()); // check if first block had a branch if (newFirstBlock != null && newFirstBlock.Statements != null && newFirstBlock.Statements.Count > 0) { // check if we need to adjust branch to last block var branch = newFirstBlock.Statements[newFirstBlock.Statements.Count - 1] as Branch; if (branch != null && branch.Target != null && branch.Target.UniqueKey == oldLastBlock.UniqueKey) { branch.Target = newLastBlock; } } } // Next extract full blocks between currentBlockIndex and including lastFullBlock for (; currentBlockIndex <= lastFullBlock; currentBlockIndex++) { var block = (Block) blocks[currentBlockIndex]; // don't skip null blocks since context relies on number clump.Add(block); if (block == null) continue; blocks[currentBlockIndex] = assumeBlock; assumeBlock = null; if (newLastBlock != null && block.Statements != null && block.Statements.Count > 0) { // check if we need to adjust branch to last block var branch = block.Statements[block.Statements.Count - 1] as Branch; if (branch != null && branch.Target != null && branch.Target.UniqueKey == oldLastBlock.UniqueKey) { branch.Target = newLastBlock; } } } // next, if last block wasn't full, we have a new last block, extract the prefix if (newLastBlock != null) { for (int i = 0; i < lastStmtIndex + 1; i++) { newLastBlock.Statements.Add(oldLastBlock.Statements[i]); oldLastBlock.Statements[i] = assumeBlock; assumeBlock = null; } clump.Add(newLastBlock); } return clump; }
/// <summary> /// Method replaces Requires to Assume in the MoveNext method of the async or iterator state machine. /// </summary> private void ReplaceRequiresWithAssumeInMoveNext(RequiresList origPreconditions, AssumeBlock originalContractPosition) { Contract.Assert(origPreconditions != null); if (originalContractPosition != null && originalContractPosition.Statements != null /*&& origPreconditions != null */&& origPreconditions.Count > 0) { var origStatements = originalContractPosition.Statements; foreach (var pre in origPreconditions) { if (pre == null) continue; var assume = new MethodCall( new MemberBinding(null, this.contractNodes.AssumeMethod), new ExpressionList(pre.Condition), NodeType.Call); assume.SourceContext = pre.SourceContext; var assumeStmt = new ExpressionStatement(assume); assumeStmt.SourceContext = pre.SourceContext; origStatements.Add(assumeStmt); } } }