Esempio n. 1
0
    private void RecordEnsures(
        Method method, 
        ref int oldLocalUniqueNameCounter, 
        List<Block> contractInitializationBlocks, 
        Dictionary<TypeNode, Local> closureLocals, 
        Block oldExpressionPreStateValues, 
        List<Ensures> postconditions, 
        List<Ensures> asyncPostconditions, 
        WrapParametersInOldExpressions wrap, 
        ref EmitAsyncClosure asyncBuilder,
        MethodContract mc)
    {
        RewriteHelper.RecordClosureInitialization(method, mc.ContractInitializer, closureLocals);
        if (this.Emit(RuntimeContractEmitFlags.Ensures) && mc.Ensures != null)
        {
            mc.Ensures = wrap.VisitEnsuresList(mc.Ensures);
            Block oldInits = ProcessOldExpressions(method, mc.Ensures, closureLocals, ref oldLocalUniqueNameCounter);
            if (oldInits != null)
            {
                oldExpressionPreStateValues.Statements.Add(oldInits);
            }
            if (mc.Ensures != null)
            {
                foreach (Ensures ensures in mc.Ensures)
                {
                    if (!EmitEnsures(ensures, method.DeclaringType, this.skipQuantifiers)) continue;
                    postconditions.Add(ensures);
                }
            }
        }
        if (this.Emit(RuntimeContractEmitFlags.AsyncEnsures) && mc.AsyncEnsuresCount > 0)
        {
            mc.AsyncEnsures = wrap.VisitEnsuresList(mc.AsyncEnsures);
            var found = false;
            foreach (Ensures postcondition in mc.AsyncEnsures)
            {
                if (!EmitEnsures(postcondition, method.DeclaringType, this.skipQuantifiers) || asyncPostconditions.Contains(postcondition)) continue;

                EnsureAsyncBuilder(method, contractInitializationBlocks, closureLocals, ref asyncBuilder);
                found = true;
                asyncPostconditions.Add(postcondition);
            }
            if (found)
            {
                Block oldInit = ProcessOldExpressionsInAsync(method, mc.AsyncEnsures, closureLocals, ref oldLocalUniqueNameCounter, asyncBuilder.ClosureClass);
                if (oldInit != null && oldInit.Statements != null && oldInit.Statements.Count > 0)
                {
                    oldExpressionPreStateValues.Statements.Add(oldInit);
                }
            }
        }
    }
Esempio n. 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
    }