Beispiel #1
0
        public void AddAsyncPost(List<Ensures> asyncPostconditions)
        {
            var origBody = new Block(this.checkBody);
            origBody.HasLocals = true;

            var newBodyBlock = new Block(new StatementList());
            newBodyBlock.HasLocals = true;

            var methodBody = new StatementList();
            var methodBodyBlock = new Block(methodBody);
            methodBodyBlock.HasLocals = true;
            checkMethod.Body = methodBodyBlock;

            methodBody.Add(newBodyBlock);
            Block newExitBlock = new Block();
            methodBody.Add(newExitBlock);

            #region Map closure locals to fields and initialize closure fields

            foreach (Ensures e in asyncPostconditions)
            {
                if (e == null) continue;
                this.Visit(e);
                if (this.forwarder != null) { this.forwarder.Visit(e); }
                ReplaceResult repResult = new ReplaceResult(this.checkMethod, this.originalResultLocal, this.parent.assemblyBeingRewritten);
                repResult.Visit(e);
                // now need to initialize closure result fields
                foreach (var target in repResult.NecessaryResultInitializationAsync(this.closureLocals))
                {
                    // note: target here 
                    methodBody.Add(new AssignmentStatement(target, this.originalResultLocal));
                }
            }

            #endregion

            #region Emit normal postconditions

            SourceContext lastEnsuresSourceContext = default(SourceContext);
            bool hasLastEnsuresContext = false;
            bool containsExceptionalPostconditions = false;
            var ensuresChecks = new StatementList();
            foreach (Ensures e in asyncPostconditions)
            {
                // Exceptional postconditions are handled separately.
                if (e is EnsuresExceptional)
                {
                    containsExceptionalPostconditions = true;
                    continue;
                }

                if (IsVoidTask()) break; // something is wrong in the original contract

                lastEnsuresSourceContext = e.SourceContext;
                hasLastEnsuresContext = true;
                // call Contract.RewriterEnsures
                Method ensMethod = this.parent.runtimeContracts.EnsuresMethod;
                ExpressionList args = new ExpressionList();
                args.Add(e.PostCondition);
                if (e.UserMessage != null)
                    args.Add(e.UserMessage);
                else
                    args.Add(Literal.Null);
                if (e.SourceConditionText != null)
                {
                    args.Add(e.SourceConditionText);
                }
                else
                {
                    args.Add(Literal.Null);
                }
                ensuresChecks.Add(new ExpressionStatement(
                  new MethodCall(new MemberBinding(null, ensMethod),
                  args, NodeType.Call, SystemTypes.Void), e.SourceContext));
            }
            this.parent.cleanUpCodeCoverage.VisitStatementList(ensuresChecks);
            this.parent.EmitRecursionGuardAroundChecks(this.checkMethod, methodBodyBlock, ensuresChecks);
            #endregion Normal postconditions

            #region Exceptional postconditions
            if (containsExceptionalPostconditions)
            {
                // Because async tasks wrap exceptions into Aggregate exceptions, we have to catch AggregateException
                // and iterate over the internal exceptions of the Aggregate.

                // We thus emit the following handler:
                //
                //  catch(AggregateException ae) {
                //    ae.Handle(this.CheckException);
                //    rethrow;
                //  }
                //
                //  alternatively, if the Task has no Result, we emit
                //
                //    var ae = t.Exception as AggregateException;
                //    if (ae != null) {
                //      ae.Handle(this.CheckException);
                //      throw ae;
                //    }

                var aggregateType = AggregateExceptionType.Value;

                var exnParameter = new Parameter(Identifier.For("e"), SystemTypes.Exception);
                this.checkExceptionMethod = new Method(this.closureClass, null, this.checkExceptionMethodId, new ParameterList(exnParameter), SystemTypes.Boolean, new Block(new StatementList()));
                this.checkExceptionMethod.Body.HasLocals = true;
                checkExceptionMethod.CallingConvention = CallingConventionFlags.HasThis;
                checkExceptionMethod.Flags |= MethodFlags.Public;

                this.closureClass.Members.Add(checkExceptionMethod);

                if (this.IsVoidTask())
                {
                    var blockAfterTest = new Block();
                    methodBody.Add(origBody);
                    methodBody.Add(new Branch(new UnaryExpression(this.newResultLocal, NodeType.LogicalNot), blockAfterTest));
                    var funcType = Func2Type.Value;
                    funcType = funcType.GetTemplateInstance(this.parent.assemblyBeingRewritten, SystemTypes.Exception, SystemTypes.Boolean);
                    var handleMethod = aggregateType.GetMethod(Identifier.For("Handle"), funcType);

                    var funcLocal = new Local(funcType);
                    var ldftn = new UnaryExpression(new MemberBinding(null, this.checkExceptionMethod), NodeType.Ldftn, CoreSystemTypes.IntPtr);
                    methodBody.Add(new AssignmentStatement(funcLocal, new Construct(new MemberBinding(null, funcType.GetConstructor(SystemTypes.Object, SystemTypes.IntPtr)), new ExpressionList(this.checkMethod.ThisParameter, ldftn))));
                    methodBody.Add(new ExpressionStatement(new MethodCall(new MemberBinding(this.newResultLocal, handleMethod), new ExpressionList(funcLocal))));
                    methodBody.Add(new Throw(this.newResultLocal));
                    methodBody.Add(blockAfterTest);
                }
                else
                {
                    // The tryCatch holds the try block, the catch blocks, and an empty block that is the
                    // target of an unconditional branch for normal execution to go from the try block
                    // around the catch blocks.
                    if (this.checkMethod.ExceptionHandlers == null) this.checkMethod.ExceptionHandlers = new ExceptionHandlerList();

                    Block afterCatches = new Block(new StatementList());

                    Block tryCatch = newBodyBlock;
                    Block tryBlock = new Block(new StatementList());
                    tryBlock.Statements.Add(origBody);
                    tryBlock.Statements.Add(new Branch(null, afterCatches, false, true, true));
                    // the EH needs to have a pointer to this block so the writer can
                    // calculate the length of the try block. So it should be the *last*
                    // thing in the try body.
                    Block blockAfterTryBody = new Block(null);
                    tryBlock.Statements.Add(blockAfterTryBody);
                    tryCatch.Statements.Add(tryBlock);
                    // The catchBlock contains the catchBody, and then
                    // an empty block that is used in the EH.

                    Block catchBlock = new Block(new StatementList());
                    Local l = new Local(aggregateType);
                    Throw rethrow = new Throw();
                    rethrow.NodeType = NodeType.Rethrow;

                    catchBlock.Statements.Add(new AssignmentStatement(l, new Expression(NodeType.Pop)));

                    var funcType = Func2Type.Value;
                    funcType = funcType.GetTemplateInstance(this.parent.assemblyBeingRewritten, SystemTypes.Exception, SystemTypes.Boolean);
                    var handleMethod = aggregateType.GetMethod(Identifier.For("Handle"), funcType);

                    var funcLocal = new Local(funcType);
                    var ldftn = new UnaryExpression(new MemberBinding(null, this.checkExceptionMethod), NodeType.Ldftn, CoreSystemTypes.IntPtr);
                    catchBlock.Statements.Add(new AssignmentStatement(funcLocal, new Construct(new MemberBinding(null, funcType.GetConstructor(SystemTypes.Object, SystemTypes.IntPtr)), new ExpressionList(this.checkMethod.ThisParameter, ldftn))));
                    catchBlock.Statements.Add(new ExpressionStatement(new MethodCall(new MemberBinding(l, handleMethod), new ExpressionList(funcLocal))));
                    // Handle method should return if all passes
                    catchBlock.Statements.Add(rethrow);
                    // The last thing in each catch block is an empty block that is the target of
                    // BlockAfterHandlerEnd in each exception handler.
                    // It is used in the writer to determine the length of each catch block
                    // so it should be the last thing added to each catch block.
                    Block blockAfterHandlerEnd = new Block(new StatementList());
                    catchBlock.Statements.Add(blockAfterHandlerEnd);
                    tryCatch.Statements.Add(catchBlock);

                    // add information to the ExceptionHandlers of this method
                    ExceptionHandler exHandler = new ExceptionHandler();
                    exHandler.TryStartBlock = origBody;
                    exHandler.BlockAfterTryEnd = blockAfterTryBody;
                    exHandler.HandlerStartBlock = catchBlock;
                    exHandler.BlockAfterHandlerEnd = blockAfterHandlerEnd;
                    exHandler.FilterType = l.Type;
                    exHandler.HandlerType = NodeType.Catch;
                    this.checkMethod.ExceptionHandlers.Add(exHandler);

                    tryCatch.Statements.Add(afterCatches);
                }
                EmitCheckExceptionBody(asyncPostconditions);
            }
            else // no exceptional post conditions
            {
                newBodyBlock.Statements.Add(origBody);
            }
            #endregion Exceptional and finally postconditions


            #region Create a block for the return statement and insert it
            // this is the block that contains the return statements
            // it is (supposed to be) the single exit from the method
            // that way, anything that must always be done can be done
            // in this block
            Statement returnStatement;
            if (this.IsVoidTask())
            {
                returnStatement = new Return();
            }
            else
            {
                returnStatement = new Return(this.newResultLocal);
            }
            if (hasLastEnsuresContext)
            {
                returnStatement.SourceContext = lastEnsuresSourceContext;
            }
            Block returnBlock = new Block(new StatementList(1));
            returnBlock.Statements.Add(returnStatement);
            methodBody.Add(returnBlock);
            #endregion
        }
Beispiel #2
0
    //[ContractVerification(true)]
    private void VisitMethodInternal(Method method) {
      if (method.IsAbstract || IsRewriterGenerated(method)) {
        return;
      }
      Block origBody = method.Body;
      if (origBody == null || origBody.Statements == null || origBody.Statements.Count == 0) return;

#if DEBUG
      if (!RewriteHelper.PostNormalizedFormat(method))
        throw new RewriteException("The method body for '" + method.FullName + "' is not structured correctly");
#endif


      #region Rewrite all Assert and Assume methods
      // Happens in-place so any modifications made are persisted even if nothing else is done to method

      RewriteAssertAssumeAndCallSiteRequires raa = new RewriteAssertAssumeAndCallSiteRequires(this.AssertAssumeRewriteMethodTable, this.runtimeContracts, this.contractEmitFlags, this, method);
      method.Body = raa.VisitBlock(method.Body);

      #endregion Rewrite all Assert and Assume methods (if user specified)

      #region Bail out early if publicSurfaceOnly and method is not visible (but not for contract validators!) or contract abbreviator
      if (this.runtimeContracts.PublicSurfaceOnly && !IsCallableFromOutsideAssembly(method) && !ContractNodes.IsValidatorMethod(method)
          || ContractNodes.IsAbbreviatorMethod(method)) return;
      #endregion

      int oldLocalUniqueNameCounter = 0;
      List<Block> contractInitializationBlocks = new List<Block>();
      Block postPreambleBlock = null;
      Dictionary<TypeNode, Local> closureLocals = new Dictionary<TypeNode, Local>();
      Block oldExpressionPreStateValues = new Block(new StatementList());
      #region Gather pre- and postconditions from supertypes and from method contract
      // Create lists of unique preconditions and postconditions.
      List<Requires> preconditions = new List<Requires>();
      List<Ensures> postconditions = new List<Ensures>();
      List<Ensures> asyncPostconditions = new List<Ensures>();
      RequiresList validations = null;
      // For postconditions, wrap all parameters within an old expression (if they are not already within one)
      var wrap = new WrapParametersInOldExpressions();
      EmitAsyncClosure asyncBuilder = null;

      #region Copy the method's contracts.
      if (method.Contract != null && (method.Contract.RequiresCount > 0 || method.Contract.EnsuresCount > 0 || 
                                      method.Contract.ValidationsCount > 0 || method.Contract.AsyncEnsuresCount > 0))
      {
        // Use duplicate of contract because it is going to get modified from processing Old(...) and
        // Result(). Can't have the modified contract get inherited (or else the Old(...) processing
        // will not work properly.)
        // Can't use MethodContract.CopyFrom or HelperMethods.DuplicateMethodBodyAndContract
        // because they assume the new contract is in a different method. Plus the latter duplicates
        // any closure classes needed for anonymous delegates in the contract.
        MethodContract mc = HelperMethods.DuplicateContractAndClosureParts(method, method, this.runtimeContracts.ContractNodes, true);
        validations = mc.Validations;


        if (mc != null) {
          contractInitializationBlocks.Add(mc.ContractInitializer);
          postPreambleBlock = mc.PostPreamble;

          RecordEnsures(method, ref oldLocalUniqueNameCounter, contractInitializationBlocks, closureLocals, oldExpressionPreStateValues, postconditions, asyncPostconditions, wrap, ref asyncBuilder, mc);

        }
      }
      #endregion Copy the method's contracts.
      #region Search the class hierarchy for overridden methods to propagate their contracts.
      if (this.Emit(RuntimeContractEmitFlags.InheritContracts) && method.OverridesBaseClassMember && method.DeclaringType != null && method.OverriddenMethod != null) {
        for (TypeNode super = method.DeclaringType.BaseType; super != null; super = HelperMethods.DoesInheritContracts(super)?super.BaseType:null) {
          var baseMethod = super.GetImplementingMethod(method.OverriddenMethod, false);
          Method baseContractMethod = HelperMethods.GetContractMethod(baseMethod);
          if (baseContractMethod != null) {
            MethodContract mc = HelperMethods.DuplicateContractAndClosureParts(method, baseContractMethod, this.runtimeContracts.ContractNodes, false);
            RewriteHelper.ReplacePrivateFieldsThatHavePublicProperties(method.DeclaringType, super, mc, this.rewriterNodes);
            if (mc != null) {
              contractInitializationBlocks.Add(mc.ContractInitializer);
              // can't have post preambles in overridden methods, since they cannot be constructors.

              // only add requires if baseMethod is the root method
              if (mc.Requires != null && baseMethod.OverriddenMethod == null)
              {
                foreach (RequiresPlain requires in mc.Requires) {
                  if (!EmitRequires(requires, this.skipQuantifiers)) continue;
                  // Debug.Assert(!preconditions.Contains(requires));
                  preconditions.Add(requires);
                }
              }
              RecordEnsures(method, ref oldLocalUniqueNameCounter, contractInitializationBlocks, closureLocals, oldExpressionPreStateValues, postconditions, asyncPostconditions, wrap, ref asyncBuilder, mc);
            }
          }

          // bail out if this base type does not inherit contracts
          if (!HelperMethods.DoesInheritContracts(super)) break;
        }
      }
      #endregion Search the class hierarchy for overridden methods to propagate their contracts.
      #region Propagate explicit interface method contracts.
      if (this.Emit(RuntimeContractEmitFlags.InheritContracts) && method.ImplementedInterfaceMethods != null) {
        foreach (Method interfaceMethod in method.ImplementedInterfaceMethods) {
          if (interfaceMethod != null)
          {
            Method contractMethod = HelperMethods.GetContractMethod(interfaceMethod);

            if (contractMethod != null)
            { // if null, then no contract for this interface method

              // Maybe it would be easier to just duplicate the entire method and then pull the
              // initialization code from the duplicate?
              MethodContract mc=HelperMethods.DuplicateContractAndClosureParts(method, contractMethod, this.runtimeContracts.ContractNodes, false);
              if (mc != null)
              {
                contractInitializationBlocks.Add(mc.ContractInitializer);
                // can't have post preambles in interface methods (not constructors)
                if (mc.Requires != null)
                {
                  foreach (RequiresPlain requires in mc.Requires)
                  {
                    if (!EmitRequires(requires, this.skipQuantifiers)) continue;
                    // Debug.Assert(!preconditions.Contains(requires));
                    preconditions.Add(requires);
                  }
                }
                RecordEnsures(method, ref oldLocalUniqueNameCounter, contractInitializationBlocks, closureLocals, oldExpressionPreStateValues, postconditions, asyncPostconditions, wrap, ref asyncBuilder, mc);
              }
            }
          }
        }
      }
      #endregion Propagate explicit interface method contracts.
      #region Propagate implicit interface method contracts.
      if (this.Emit(RuntimeContractEmitFlags.InheritContracts) && method.ImplicitlyImplementedInterfaceMethods != null) {
        foreach (Method interfaceMethod in method.ImplicitlyImplementedInterfaceMethods) {
          if (interfaceMethod != null) {
            Method contractMethod = HelperMethods.GetContractMethod(interfaceMethod);
            if (contractMethod != null)
            { // if null, then no contract for this method


              // Maybe it would be easier to just duplicate the entire method and then pull the
              // initialization code from the duplicate?
              MethodContract mc = HelperMethods.DuplicateContractAndClosureParts(method, contractMethod, this.runtimeContracts.ContractNodes, false);
              if (mc != null)
              {
                contractInitializationBlocks.Add(mc.ContractInitializer);
                // can't have post preambles in implicit interface method implementations, as they are not constructors.
                if (mc.Requires != null)
                {
                  foreach (RequiresPlain requires in mc.Requires)
                  {
                    if (!EmitRequires(requires, this.skipQuantifiers)) continue;
                    // Debug.Assert(!preconditions.Contains(requires));
                    preconditions.Add(requires);
                  }
                }
                RecordEnsures(method, ref oldLocalUniqueNameCounter, contractInitializationBlocks, closureLocals, oldExpressionPreStateValues, postconditions, asyncPostconditions, wrap, ref asyncBuilder, mc);
              }
            }
          }
        }
      }
      #endregion Propagate implicit interface method contracts.
      #endregion Gather pre- and postconditions from supertypes and from method contract


      #region Return if there is nothing to do
      if (preconditions.Count < 1 && postconditions.Count < 1 && asyncPostconditions.Count < 1 && (validations == null || validations.Count == 0) && this.InvariantMethod == null)
        return;
      #endregion Return if there is nothing to do

      #region Change all short branches to long because code modifications can add code
      {
        // REVIEW: This does *not* remove any short branches in the contracts.
        // I think that is okay, but we should think about it.
        RemoveShortBranches rsb = new RemoveShortBranches();
        rsb.VisitBlock(method.Body);
      }
      #endregion Change all short branches to long because code modifications can add code
      #region Modify method body to change all returns into assignments and branch to unified exit
      // now modify the method body to change all return statements
      // into assignments to the local "result" and a branch to a
      // block at the end that can check the post-condition using
      // "result" and then finally return it.
      // [MAF] we do it later once we know if the branches are leaves or just branch.
      Local result = null;
      if (!HelperMethods.IsVoidType(method.ReturnType)) {
        // don't write huge names. The debugger chokes on them and shows no locals at all.
        //   result = new Local(Identifier.For("Contract.Result<" + typeName + ">()"), method.ReturnType);
        result = new Local(Identifier.For("Contract.Result()"), method.ReturnType);
        if (method.LocalList == null) {
          method.LocalList = new LocalList();
        }
        method.LocalList.Add(result);
      }
      Block newExit = new Block(new StatementList());
      #endregion Modify method body to change all returns into assignments and branch to unified exit

      #region Create the new method's body
      Block newBody = new Block(new StatementList());
      newBody.HasLocals = true;

      #endregion Create the new method's body
      #region If there had been any closure initialization code, put it first in the new body
      if (0 < contractInitializationBlocks.Count) {
        foreach (Block b in contractInitializationBlocks) {
          newBody.Statements.Add(b);
        }
      }
      #endregion

      EmitInterleavedValidationsAndRequires(method, preconditions, validations, newBody);

      #region Turn off invariant checking until the end
      Local oldReEntrancyFlagLocal = null;
      if (MethodShouldHaveInvariantChecked(method) && this.ReentrancyFlag != null && BodyHasCalls(method.Body))
      {
        oldReEntrancyFlagLocal = new Local(SystemTypes.Boolean);
        newBody.Statements.Add(new Block(new StatementList(
          new AssignmentStatement(oldReEntrancyFlagLocal, new MemberBinding(method.ThisParameter, this.ReentrancyFlag)),
          new AssignmentStatement(new MemberBinding(method.ThisParameter, this.ReentrancyFlag), Literal.True))));
      }
      #endregion

      #region Put all of the collected initializations from "old" expressions into the method
      if (oldExpressionPreStateValues.Statements.Count > 0) {
        newBody.Statements.Add(oldExpressionPreStateValues);
      }
      #endregion

      // if there are preamble blocks we need to move them from the origBody in case we will wrap a try-catch
      var preambleIndex = 0;
      while (origBody.Statements.Count > preambleIndex && origBody.Statements[preambleIndex] is PreambleBlock) {
        newBody.Statements.Add(origBody.Statements[preambleIndex]);
        origBody.Statements[preambleIndex] = null;
        preambleIndex++;
      }
      Block newBodyBlock = new Block(new StatementList());
      newBody.Statements.Add(newBodyBlock); // placeholder for eventual body
      newBody.Statements.Add(newExit);

      #region Replace "result" in postconditions (both for method return and out parameters)
      if (result != null)
      {
        foreach (Ensures e in postconditions)
        {
          if (e == null) continue;
          ReplaceResult repResult = new ReplaceResult(method, result, this.assemblyBeingRewritten);
          repResult.Visit(e);
          // now need to initialize closure result fields
          foreach (var target in repResult.NecessaryResultInitialization(closureLocals))
          {
            newBody.Statements.Add(new AssignmentStatement(target, result));
          }
        }
      }
      #endregion

      #region Emit potential post preamble block (from contract duplicate) in constructors
      if (postPreambleBlock != null)
      {
        newBody.Statements.Add(postPreambleBlock);
      }
      #endregion

      #region Emit normal postconditions

      SourceContext lastEnsuresSourceContext = default(SourceContext);
      bool hasLastEnsuresContext = false;
      bool containsExceptionalPostconditions = false;
      var ensuresChecks = new StatementList();
      foreach (Ensures e in postconditions) {
        // Exceptional postconditions are handled separately.
        if (e is EnsuresExceptional) {
          containsExceptionalPostconditions = true;
          continue;
        }

        lastEnsuresSourceContext = e.SourceContext;
        hasLastEnsuresContext = true;
        // call Contract.RewriterEnsures
        Method ensMethod = this.runtimeContracts.EnsuresMethod;
        ExpressionList args = new ExpressionList();
        args.Add(e.PostCondition);
        if (e.UserMessage != null)
          args.Add(e.UserMessage);
        else
          args.Add(Literal.Null);
        if (e.SourceConditionText != null) {
          args.Add(e.SourceConditionText);
        } else {
          args.Add(Literal.Null);
        }
        ensuresChecks.Add(new ExpressionStatement(
          new MethodCall(new MemberBinding(null, ensMethod),
          args, NodeType.Call, SystemTypes.Void), e.SourceContext));
      }
      this.cleanUpCodeCoverage.VisitStatementList(ensuresChecks);
      EmitRecursionGuardAroundChecks(method, newBody, ensuresChecks);
      #endregion Normal postconditions

      #region Emit object invariant
      if (MethodShouldHaveInvariantChecked(method)) {
        // Now turn checking on by restoring old reentrancy flag
        if (this.ReentrancyFlag != null && oldReEntrancyFlagLocal != null) 
        {
          newBody.Statements.Add(new AssignmentStatement(new MemberBinding(method.ThisParameter, this.ReentrancyFlag), oldReEntrancyFlagLocal));
        }
        var callType = (method is InstanceInitializer) || this.InvariantMethod.DeclaringType.IsValueType ? NodeType.Call : NodeType.Callvirt;

        // just add a call to the already existing invariant method, "this.InvariantMethod();"
        // all of the processing needed is done as part of VisitClass
        newBody.Statements.Add(
          new ExpressionStatement(
          new MethodCall(
          new MemberBinding(method.ThisParameter, this.InvariantMethod), null, callType, SystemTypes.Void)));
      }
      #endregion Object invariant

      #region Emit exceptional postconditions

      ReplaceReturns rr;
      if (containsExceptionalPostconditions)
      {
        // -- The following code comes from System.Compiler.Normalizer.CreateTryCatchBlock --

        // The tryCatch holds the try block, the catch blocks, and an empty block that is the
        // target of an unconditional branch for normal execution to go from the try block
        // around the catch blocks.
        if (method.ExceptionHandlers == null) method.ExceptionHandlers = new ExceptionHandlerList();

        Block afterCatches = new Block(new StatementList());

        Block tryCatch = newBodyBlock;
        Block tryBlock = new Block(new StatementList());
        tryBlock.Statements.Add(origBody);
        rr = new ReplaceReturns(result, newExit, leaveExceptionBody:true);
        rr.Visit(origBody);

        tryBlock.Statements.Add(new Branch(null, afterCatches, false, true, true));
        // the EH needs to have a pointer to this block so the writer can
        // calculate the length of the try block. So it should be the *last*
        // thing in the try body.
        Block blockAfterTryBody = new Block(null);
        tryBlock.Statements.Add(blockAfterTryBody);
        tryCatch.Statements.Add(tryBlock);
        for (int i = 0, n = postconditions.Count; i < n; i++)
        {
          // Normal postconditions are handled separately.
          EnsuresExceptional e = postconditions[i] as EnsuresExceptional;
          if (e == null)
            continue;

          // The catchBlock contains the catchBody, and then
          // an empty block that is used in the EH.
          Block catchBlock = new Block(new StatementList());
          Local l = new Local(e.Type);
          Throw rethrow = new Throw();
          rethrow.NodeType = NodeType.Rethrow;

          // call Contract.RewriterEnsures
          ExpressionList args = new ExpressionList();
          args.Add(e.PostCondition);
          if (e.UserMessage != null)
            args.Add(e.UserMessage);
          else
            args.Add(Literal.Null);
          if (e.SourceConditionText != null)
          {
            args.Add(e.SourceConditionText);
          }
          else
          {
            args.Add(Literal.Null);
          }
          args.Add(l);
          var checks = new StatementList();
          checks.Add(new ExpressionStatement(
            new MethodCall(new MemberBinding(null, this.runtimeContracts.EnsuresOnThrowMethod),
            args, NodeType.Call, SystemTypes.Void), e.SourceContext));

          catchBlock.Statements.Add(new AssignmentStatement(l, new Expression(NodeType.Pop), e.SourceContext));
          this.cleanUpCodeCoverage.VisitStatementList(checks);
          EmitRecursionGuardAroundChecks(method, catchBlock, checks);

          #region Emit object invariant on EnsuresOnThrow check
          if (MethodShouldHaveInvariantChecked(method, inExceptionCase: true))
          {
            // Now turn checking on by restoring old reentrancy flag
            if (this.ReentrancyFlag != null && oldReEntrancyFlagLocal != null)
            {
              catchBlock.Statements.Add(new AssignmentStatement(new MemberBinding(method.ThisParameter, this.ReentrancyFlag), oldReEntrancyFlagLocal));
            }
            // just add a call to the already existing invariant method, "this.InvariantMethod();"
            // all of the processing needed is done as part of VisitClass
            catchBlock.Statements.Add(
              new ExpressionStatement(
              new MethodCall(
              new MemberBinding(method.ThisParameter, this.InvariantMethod), null, NodeType.Call, SystemTypes.Void)));
          }
          #endregion Object invariant

          catchBlock.Statements.Add(rethrow);
          // The last thing in each catch block is an empty block that is the target of
          // BlockAfterHandlerEnd in each exception handler.
          // It is used in the writer to determine the length of each catch block
          // so it should be the last thing added to each catch block.
          Block blockAfterHandlerEnd = new Block(new StatementList());
          catchBlock.Statements.Add(blockAfterHandlerEnd);
          tryCatch.Statements.Add(catchBlock);

          // add information to the ExceptionHandlers of this method
          ExceptionHandler exHandler = new ExceptionHandler();
          exHandler.TryStartBlock = origBody;
          exHandler.BlockAfterTryEnd = blockAfterTryBody;
          exHandler.HandlerStartBlock = catchBlock;
          exHandler.BlockAfterHandlerEnd = blockAfterHandlerEnd;
          exHandler.FilterType = l.Type;
          exHandler.HandlerType = NodeType.Catch;
          method.ExceptionHandlers.Add(exHandler);
        }
        tryCatch.Statements.Add(afterCatches);
      }
      else // no exceptional post conditions
      {
        newBodyBlock.Statements.Add(origBody);
        rr = new ReplaceReturns(result, newExit, leaveExceptionBody: false);
        rr.Visit(origBody);
      }
      #endregion Exceptional and finally postconditions

      #region Create a block for the return statement and insert it
      // this is the block that contains the return statements
      // it is (supposed to be) the single exit from the method
      // that way, anything that must always be done can be done
      // in this block
      Block returnBlock = new Block(new StatementList(1));

      if (asyncPostconditions != null && asyncPostconditions.Count > 0)
      {
          asyncBuilder.AddAsyncPost(asyncPostconditions);
          var funcLocal = new Local(asyncBuilder.FuncCtor.DeclaringType);
          var ldftn = new UnaryExpression(new MemberBinding(null, asyncBuilder.CheckMethod), NodeType.Ldftn, CoreSystemTypes.IntPtr);
          returnBlock.Statements.Add(new AssignmentStatement(funcLocal, new Construct(new MemberBinding(null, asyncBuilder.FuncCtor), new ExpressionList(asyncBuilder.ClosureLocal, ldftn))));
          returnBlock.Statements.Add(new AssignmentStatement(result, new MethodCall(new MemberBinding(result, asyncBuilder.ContinueWithMethod), new ExpressionList(funcLocal))));
      }
      Statement returnStatement;
      if (!HelperMethods.IsVoidType(method.ReturnType)) {
        returnStatement = new Return(result);
      }
      else
      {
        returnStatement = new Return();
      }
      if (hasLastEnsuresContext)
      {
        returnStatement.SourceContext = lastEnsuresSourceContext;
      }
      else
      {
        returnStatement.SourceContext = rr.LastReturnSourceContext;
      }
      returnBlock.Statements.Add(returnStatement);
      newBody.Statements.Add(returnBlock);
      #endregion

      method.Body = newBody;

      #region Make sure InitLocals is marked for this method
      // 15 April 2003
      // Since each method has locals added to it, need to make sure this flag is
      // on. Otherwise, the generated code cannot pass peverify.
      if (!method.InitLocals) {
        method.InitLocals = true;
        //WriteToLog("Setting InitLocals for method: {0}", method.FullName);
      }
      #endregion
    }
Beispiel #3
0
        private ReplaceResult ReplaceResult(Method method, Local result, Ensures e)
        {
            ReplaceResult repResult = new ReplaceResult(method, result, this.assemblyBeingRewritten);
            repResult.Visit(e);

            if (repResult.ContractResultWasCapturedInStaticContext)
            {
                this.HandleError(CreateContractResultWasCapturedInStaticContextWarning(e.Assertion.SourceContext));
            }

            return repResult;
        }
        private void AddAsyncPost(List <Ensures> asyncPostconditions)
        {
            var origBody = new Block(this.checkPostBody);

            origBody.HasLocals = true;

            var newBodyBlock = new Block(new StatementList());

            newBodyBlock.HasLocals = true;

            var methodBody      = new StatementList();
            var methodBodyBlock = new Block(methodBody);

            methodBodyBlock.HasLocals = true;

            checkPostMethod.Body = methodBodyBlock;

            methodBody.Add(newBodyBlock);
            Block newExitBlock = new Block();

            methodBody.Add(newExitBlock);

            // Map closure locals to fields and initialize closure fields

            foreach (Ensures e in asyncPostconditions)
            {
                if (e == null)
                {
                    continue;
                }

                this.Visit(e);

                if (this.forwarder != null)
                {
                    this.forwarder.Visit(e);
                }

                ReplaceResult repResult = new ReplaceResult(
                    this.checkPostMethod, this.originalResultLocal,
                    this.rewriter.AssemblyBeingRewritten);

                repResult.Visit(e);

                if (repResult.ContractResultWasCapturedInStaticContext)
                {
                    this.contractResultCapturedInStaticContext.Add(e.Assertion.SourceContext);
                }

                // now need to initialize closure result fields
                foreach (var target in repResult.NecessaryResultInitializationAsync(this.closureLocals))
                {
                    // note: target here
                    methodBody.Add(new AssignmentStatement(target, this.originalResultLocal));
                }
            }

            // Emit normal postconditions

            SourceContext?lastEnsuresSourceContext = null;

            var ensuresChecks = new StatementList();

            Method contractEnsuresMethod = this.rewriter.RuntimeContracts.EnsuresMethod;

            foreach (Ensures e in GetTaskResultBasedEnsures(asyncPostconditions))
            {
                // TODO: Not sure that 'break' is enough! It seems that this is possible
                // only when something is broken, because normal postconditions
                // are using Contract.Result<T>() and this is possible only for
                // generic tasks.
                if (IsVoidTask())
                {
                    break;               // something is wrong in the original contract
                }
                lastEnsuresSourceContext = e.SourceContext;

                //
                // Call Contract.RewriterEnsures
                //

                ExpressionList args = new ExpressionList();
                args.Add(e.PostCondition);

                args.Add(e.UserMessage ?? Literal.Null);

                args.Add(e.SourceConditionText ?? Literal.Null);

                ensuresChecks.Add(
                    new ExpressionStatement(
                        new MethodCall(
                            new MemberBinding(null, contractEnsuresMethod),
                            args, NodeType.Call, SystemTypes.Void),
                        e.SourceContext));
            }

            this.rewriter.CleanUpCodeCoverage.VisitStatementList(ensuresChecks);

            //
            // Normal postconditions
            //

            // Wrapping normal ensures into following if statement
            // if (task.Status == TaskStatus.RanToCompletion)
            // { postcondition check }
            //
            // Implementation of this stuff is a bit tricky because if-statements
            // are inverse in the IL.
            // Basically, we need to generate following code:
            // if (!(task.Status == Task.Status.RanToCompletion))
            //   goto EndOfNormalPostcondition;
            // {postcondition check}
            // EndOfNormalPostcondition:
            // {other Code}

            // Marker for EndOfNormalPostcondition
            Block endOfNormalPostcondition = new Block();

            // Generate: if (task.Status != RanToCompletion) goto endOfNormalPostcondition;
            StatementList checkStatusStatements = CreateIfTaskResultIsEqualsTo(
                checkMethodTaskParameter, TaskStatus.RanToCompletion, endOfNormalPostcondition);

            methodBodyBlock.Statements.Add(new Block(checkStatusStatements));

            // Emit a check for __ContractsRuntime.insideContractEvaluation around Ensures
            this.rewriter.EmitRecursionGuardAroundChecks(this.checkPostMethod, methodBodyBlock, ensuresChecks);

            // Now, normal postconditions are written to the method body.
            // We need to add endOfNormalPostcondition block as a marker.
            methodBodyBlock.Statements.Add(endOfNormalPostcondition);

            //
            // Exceptional postconditions
            //

            var exceptionalPostconditions = GetExceptionalEnsures(asyncPostconditions).ToList();

            if (exceptionalPostconditions.Count > 0)
            {
                // For exceptional postconditions we need to generate CheckException method first
                Method checkExceptionMethod = CreateCheckExceptionMethod();

                EmitCheckExceptionBody(checkExceptionMethod, exceptionalPostconditions);

                this.closureClass.Members.Add(checkExceptionMethod);

                // Then, we're using the same trick as for normal postconditions:
                // wrapping exceptional postconditions only when task.Status is TaskStatus.Faulted
                Block checkExceptionBlock = new Block(new StatementList());

                // Marker for endOfExceptionPostcondition
                Block endOfExceptionPostcondition = new Block();

                StatementList checkStatusIsException = CreateIfTaskResultIsEqualsTo(
                    checkMethodTaskParameter, TaskStatus.Faulted, endOfExceptionPostcondition);

                checkExceptionBlock.Statements.Add(new Block(checkStatusIsException));

                // Now we need to emit actuall check for exceptional postconditions

                // Emit: var ae = task.Exception;

                var aeLocal = new Local(aggregateExceptionType.Value);

                checkExceptionBlock.Statements.Add(
                    new AssignmentStatement(aeLocal,
                                            new MethodCall(
                                                new MemberBinding(checkMethodTaskParameter,
                                                                  GetTaskProperty(checkMethodTaskParameter, "get_Exception")),
                                                new ExpressionList())));

                // Emit: CheckException(ae);

                // Need to store method result somewhere, otherwise stack would be corrupted
                var checkResultLocal = new Local(SystemTypes.Boolean);

                checkExceptionBlock.Statements.Add(
                    new AssignmentStatement(checkResultLocal,
                                            new MethodCall(new MemberBinding(null,
                                                                             checkExceptionMethod),
                                                           new ExpressionList(checkExceptionMethod.ThisParameter, aeLocal))));

                checkExceptionBlock.Statements.Add(endOfExceptionPostcondition);

                methodBody.Add(checkExceptionBlock);
            }

            // Copy original block to body statement for both: normal and exceptional postconditions.
            newBodyBlock.Statements.Add(origBody);

            Block returnBlock = CreateReturnBlock(checkMethodTaskParameter, lastEnsuresSourceContext);

            methodBody.Add(returnBlock);
        }
        private void AddAsyncPost(List<Ensures> asyncPostconditions)
        {
            var origBody = new Block(this.checkPostBody);
            origBody.HasLocals = true;

            var newBodyBlock = new Block(new StatementList());
            newBodyBlock.HasLocals = true;

            var methodBody = new StatementList();
            var methodBodyBlock = new Block(methodBody);
            methodBodyBlock.HasLocals = true;

            checkPostMethod.Body = methodBodyBlock;

            methodBody.Add(newBodyBlock);
            Block newExitBlock = new Block();
            methodBody.Add(newExitBlock);

            // Map closure locals to fields and initialize closure fields

            foreach (Ensures e in asyncPostconditions)
            {
                if (e == null) continue;

                this.Visit(e);

                if (this.forwarder != null)
                {
                    this.forwarder.Visit(e);
                }

                ReplaceResult repResult = new ReplaceResult(
                    this.checkPostMethod, this.originalResultLocal,
                    this.rewriter.AssemblyBeingRewritten);

                repResult.Visit(e);

                if (repResult.ContractResultWasCapturedInStaticContext)
                {
                    this.contractResultCapturedInStaticContext.Add(e.Assertion.SourceContext);
                }

                // now need to initialize closure result fields
                foreach (var target in repResult.NecessaryResultInitializationAsync(this.closureLocals))
                {
                    // note: target here 
                    methodBody.Add(new AssignmentStatement(target, this.originalResultLocal));
                }
            }

            // Emit normal postconditions

            SourceContext? lastEnsuresSourceContext = null;

            var ensuresChecks = new StatementList();

            Method contractEnsuresMethod = this.rewriter.RuntimeContracts.EnsuresMethod;

            // For generic types need to 'fix' generic type parameters that are used in the closure method. 
            // See comment to the GenericTypeMapper for more details.
            TypeParameterFixerVisitor fixer = null;

            if (!this.genericTypeMapper.IsEmpty)
            {
                fixer = new TypeParameterFixerVisitor(genericTypeMapper);
            }

            foreach (Ensures e in GetTaskResultBasedEnsures(asyncPostconditions))
            {
                // TODO: Not sure that 'break' is enough! It seems that this is possible
                // only when something is broken, because normal postconditions 
                // are using Contract.Result<T>() and this is possible only for
                // generic tasks.
                if (IsVoidTask()) break; // something is wrong in the original contract

                lastEnsuresSourceContext = e.SourceContext;

                //
                // Call Contract.RewriterEnsures
                //

                ExpressionList args = new ExpressionList();
                if (fixer != null)
                {
                    fixer.Visit(e.PostCondition);
                }

                args.Add(e.PostCondition);

                args.Add(e.UserMessage ?? Literal.Null);

                args.Add(e.SourceConditionText ?? Literal.Null);

                ensuresChecks.Add(
                    new ExpressionStatement(
                        new MethodCall(
                            new MemberBinding(null, contractEnsuresMethod),
                            args, NodeType.Call, SystemTypes.Void),
                        e.SourceContext));
            }

            this.rewriter.CleanUpCodeCoverage.VisitStatementList(ensuresChecks);

            //
            // Normal postconditions
            //

            // Wrapping normal ensures into following if statement
            // if (task.Status == TaskStatus.RanToCompletion)
            // { postcondition check }
            //
            // Implementation of this stuff is a bit tricky because if-statements
            // are inverse in the IL.
            // Basically, we need to generate following code:
            // if (!(task.Status == Task.Status.RanToCompletion))
            //   goto EndOfNormalPostcondition;
            // {postcondition check}
            // EndOfNormalPostcondition:
            // {other Code}

            // Marker for EndOfNormalPostcondition
            Block endOfNormalPostcondition = new Block();

            // Generate: if (task.Status != RanToCompletion) goto endOfNormalPostcondition;
            StatementList checkStatusStatements = CreateIfTaskResultIsEqualsTo(
                checkMethodTaskParameter, TaskStatus.RanToCompletion, endOfNormalPostcondition);

            methodBodyBlock.Statements.Add(new Block(checkStatusStatements));

            // Emit a check for __ContractsRuntime.insideContractEvaluation around Ensures
            // TODO ST: there is no sense to add recursion check in async postcondition that can be checked in different thread!
            methodBodyBlock.Statements.Add(new Block(ensuresChecks));
            // Emit a check for __ContractsRuntime.insideContractEvaluation around Ensures
            //this.rewriter.EmitRecursionGuardAroundChecks(this.checkPostMethod, methodBodyBlock, ensuresChecks);

            // Now, normal postconditions are written to the method body. 
            // We need to add endOfNormalPostcondition block as a marker.
            methodBodyBlock.Statements.Add(endOfNormalPostcondition);

            //
            // Exceptional postconditions
            //

            var exceptionalPostconditions = GetExceptionalEnsures(asyncPostconditions).ToList();
            if (exceptionalPostconditions.Count > 0)
            {
                // For exceptional postconditions we need to generate CheckException method first
                Method checkExceptionMethod = CreateCheckExceptionMethod();

                EmitCheckExceptionBody(checkExceptionMethod, exceptionalPostconditions);

                this.closureClass.Members.Add(checkExceptionMethod);

                // Then, we're using the same trick as for normal postconditions:
                // wrapping exceptional postconditions only when task.Status is TaskStatus.Faulted
                Block checkExceptionBlock = new Block(new StatementList());

                // Marker for endOfExceptionPostcondition
                Block endOfExceptionPostcondition = new Block();

                StatementList checkStatusIsException = CreateIfTaskResultIsEqualsTo(
                    checkMethodTaskParameter, TaskStatus.Faulted, endOfExceptionPostcondition);

                checkExceptionBlock.Statements.Add(new Block(checkStatusIsException));

                // Now we need to emit actuall check for exceptional postconditions

                // Emit: var ae = task.Exception;
                
                var aeLocal = new Local(aggregateExceptionType.Value);

                checkExceptionBlock.Statements.Add(
                    new AssignmentStatement(aeLocal,
                        new MethodCall(
                            new MemberBinding(checkMethodTaskParameter,
                                GetTaskProperty(checkMethodTaskParameter, "get_Exception")),
                            new ExpressionList())));

                // Emit: CheckException(ae);

                // Need to store method result somewhere, otherwise stack would be corrupted
                var checkResultLocal = new Local(SystemTypes.Boolean);

                checkExceptionBlock.Statements.Add(
                    new AssignmentStatement(checkResultLocal,
                            new MethodCall(new MemberBinding(null, 
                                checkExceptionMethod),
                            new ExpressionList(checkExceptionMethod.ThisParameter, aeLocal))));

                checkExceptionBlock.Statements.Add(endOfExceptionPostcondition);
                
                methodBody.Add(checkExceptionBlock);
            }

            // Copy original block to body statement for both: normal and exceptional postconditions.
            newBodyBlock.Statements.Add(origBody);

            Block returnBlock = CreateReturnBlock(checkMethodTaskParameter, lastEnsuresSourceContext);
            methodBody.Add(returnBlock);
        }