Esempio n. 1
0
    public override void VisitTypeNode(TypeNode typeNode) {
      if (typeNode == null) return;

      if (HelperMethods.IsContractTypeForSomeOtherType(typeNode, this.rewriterNodes) != null) {
        return;
      }
      Method savedInvariantMethod = this.InvariantMethod;
      Field savedReentrancyFlag = this.ReentrancyFlag;
      this.InvariantMethod = null;
      this.ReentrancyFlag = null;
      var savedState = this.currentState;
      this.currentState = new CurrentState(typeNode);
      var savedEmitFlags = this.AdaptRuntimeOptionsBasedOnAttributes(typeNode.Attributes);

      try
      {
        if (this.Emit(RuntimeContractEmitFlags.Invariants) && rewriterNodes.InvariantMethod != null)
        {
          InvariantList userWrittenInvariants = typeNode.Contract == null ? null : typeNode.Contract.Invariants;
          Class asClass = typeNode as Class;
          Field baseReentrancyFlag;
          Method baseInvariantMethod = FindAndInstantiateBaseClassInvariantMethod(asClass, out baseReentrancyFlag);


          if ((userWrittenInvariants != null && 0 < userWrittenInvariants.Count) || baseInvariantMethod != null)
          {
            Field reEntrancyFlag = null;
            var isStructWithExplicitLayout = IsStructWithExplicitLayout(typeNode as Struct);
            if (isStructWithExplicitLayout)
            {
              this.HandleError(new Warning(1044, String.Format("Struct '{0}' has explicit layout and an invariant. Invariant recursion guards will not be emitted and evaluation of invariants may occur too eagerly.", typeNode.FullName), new SourceContext()));
            }
            else
            {
              #region Find or create re-entrancy flag to the class
              if (baseReentrancyFlag != null)
              {
                // grab base reEntrancyFlag
                reEntrancyFlag = baseReentrancyFlag;
              }
              else
              {
                FieldFlags reentrancyFlagProtection;
                if (typeNode.IsSealed)
                {
                  reentrancyFlagProtection = FieldFlags.Private | FieldFlags.CompilerControlled;
                }
                else if (this.InheritInvariantsAcrossAssemblies)
                {
                  reentrancyFlagProtection = FieldFlags.Family | FieldFlags.CompilerControlled;
                }
                else
                {
                  reentrancyFlagProtection = FieldFlags.FamANDAssem | FieldFlags.CompilerControlled;
                }
                reEntrancyFlag = new Field(typeNode, null, reentrancyFlagProtection, Identifier.For("$evaluatingInvariant$"), SystemTypes.Boolean, null);
                RewriteHelper.TryAddCompilerGeneratedAttribute(reEntrancyFlag);
                RewriteHelper.TryAddDebuggerBrowsableNeverAttribute(reEntrancyFlag);
                typeNode.Members.Add(reEntrancyFlag);
              }
              #endregion Add re-entrancy flag to the class
            }
            Block newBody = new Block(new StatementList(3));
            // newBody ::=
            //   if (this.$evaluatingInvariant$){
            //     return (true); // don't really return true since this is a void method, but it means the invariant is assumed to hold
            //   this.$evaluatingInvariant$ := true;
            //   try{
            //     <evaluate invariants and call base invariant method>
            //   } finally {
            //     this.$evaluatingInvariant$ := false;
            //   }
            Method invariantMethod =
              new Method(
                typeNode,
                new AttributeList(),
                Identifier.For("$InvariantMethod$"),
                null,
                SystemTypes.Void,
                newBody);
            RewriteHelper.TryAddCompilerGeneratedAttribute(invariantMethod);
            invariantMethod.CallingConvention = CallingConventionFlags.HasThis;
            if (this.InheritInvariantsAcrossAssemblies)
              invariantMethod.Flags = MethodFlags.Family | MethodFlags.Virtual;
            else
              invariantMethod.Flags = MethodFlags.FamANDAssem | MethodFlags.Virtual;
            invariantMethod.Attributes.Add(new AttributeNode(new MemberBinding(null, this.runtimeContracts.ContractNodes.InvariantMethodAttribute.GetConstructor()), null));

            #region call base class invariant
            if (baseInvariantMethod != null)
            {
              newBody.Statements.Add(
                new ExpressionStatement(
                new MethodCall(
                new MemberBinding(invariantMethod.ThisParameter, baseInvariantMethod), null, NodeType.Call, SystemTypes.Void)));
            }
            #endregion
            #region Add re-entrancy test to the method
            Block invariantExit = new Block();
            if (reEntrancyFlag != null)
            {
              Block reEntrancyTest = new Block(new StatementList());
              reEntrancyTest.Statements.Add(
                new Branch(new MemberBinding(invariantMethod.ThisParameter, reEntrancyFlag), invariantExit));
              reEntrancyTest.Statements.Add(
                new AssignmentStatement(new MemberBinding(invariantMethod.ThisParameter, reEntrancyFlag), Literal.True)
                );
              newBody.Statements.Add(reEntrancyTest);
            }
            #endregion Add re-entrancy test to the method

            Block invariantChecks = new Block(new StatementList());
            if (userWrittenInvariants != null)
            {
              #region Filter out invariants that aren't runtime checkable
              var filteredInvariants = new InvariantList();
              foreach (var i in userWrittenInvariants) {
                if (!EmitInvariant(i, this.skipQuantifiers)) continue;
                filteredInvariants.Add(i);
              }
              #endregion Filter out invariants that aren't runtime checkable
              #region Duplicate the invariants
              // need to duplicate the invariants so they aren't shared
              Duplicator d = new Duplicator(typeNode.DeclaringModule, typeNode);
              InvariantList duplicatedInvariants = d.VisitInvariantList(filteredInvariants);
              // F: seems to have the invariant duplictedInvariants != null
              Contract.Assume(duplicatedInvariants != null);

              #endregion Duplicate the invariants
              #region Rewrite the body of the invariant method
              // then we need to replace calls to Contract.Invariant with calls to Contract.RewriterInvariant
              // in the body of the invariant method
              RewriteInvariant rc = new RewriteInvariant(this.runtimeContracts);
              rc.VisitInvariantList(duplicatedInvariants);
              #endregion Rewrite the body of the invariant method
              for (int i = 0, n = duplicatedInvariants.Count; i < n; i++)
              {
                Expression e = duplicatedInvariants[i].Condition;
                var blockExpr = e as BlockExpression;
                if (blockExpr != null)
                {
                  invariantChecks.Statements.Add(blockExpr.Block);
                }
                else
                {
                  invariantChecks.Statements.Add(new ExpressionStatement(e));
                }
              }
            }
            Block finallyB = new Block(new StatementList(1));
            if (reEntrancyFlag != null)
            {
              finallyB.Statements.Add(
                new AssignmentStatement(new MemberBinding(invariantMethod.ThisParameter, reEntrancyFlag), Literal.False)
                );
            }
            this.cleanUpCodeCoverage.VisitBlock(invariantChecks);
            Block b = RewriteHelper.CreateTryFinallyBlock(invariantMethod, invariantChecks, finallyB);
            newBody.Statements.Add(b);
            newBody.Statements.Add(invariantExit);
            newBody.Statements.Add(new Return());

            // set the field "this.InvariantMethod" to this one so it is used in calls within each method
            // but don't add it yet to the class so it doesn't get visited!
            this.InvariantMethod = invariantMethod;
            this.ReentrancyFlag = reEntrancyFlag;
          }
        }

        base.VisitTypeNode(typeNode);
        // Now add invariant method to the class
        if (this.InvariantMethod != null)
        {
          typeNode.Members.Add(this.InvariantMethod);
        }
        return;
      }
      finally
      {
        this.InvariantMethod = savedInvariantMethod;
        this.ReentrancyFlag = savedReentrancyFlag;
        this.currentState = savedState;
        this.contractEmitFlags = savedEmitFlags;
      }
    }