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; } }