/// <summary> /// Implements the runtime check for invariants of class C. Also implements the runtime check for modelfields. /// ensures: If c declares invariants or modelfields, then c.Contract.InvariantMethod contains their runtime checking code; /// Adds the CheckInvariant method to c.Members if it wasn't a member already. /// </summary> void ImplementCheckInvariantMethod(Class c) { // c is the class to which all of this code is going to get attached to. Method m = null; #region Get a handle m on the invariant method, create one if necessary if (c.Contract == null || c.Contract.InvariantMethod == null) { Method invariantMethod = new Method( c, new AttributeList(), Identifier.For("SpecSharp::CheckInvariant"), new ParameterList(new Parameter(Identifier.For("throwException"), SystemTypes.Boolean)), SystemTypes.Boolean, null); invariantMethod.CallingConvention = CallingConventionFlags.HasThis; invariantMethod.Flags = MethodFlags.Private; m = invariantMethod; } else m = c.Contract.InvariantMethod; #endregion Get a handle on the invariant method, create one if necessary StatementList stmts = new StatementList(); #region Create code for all of the invariants, implicit and explicit. Add that code to stmts. Parameter throwException = m.Parameters[0]; InvariantList consolidatedInvariants = new InvariantList(); InvariantList invariants = c.Contract == null ? null : c.Contract.Invariants; for (int i = 0, n = invariants == null ? 0 : invariants.Count; i < n; i++) if (!invariants[i].IsStatic) consolidatedInvariants.Add(invariants[i]); // InterfaceList ifaces = this.GetTypeView(c).Interfaces; InterfaceList ifaces = c.Interfaces; for (int i = 0, n = ifaces.Count; i < n; i++){ Interface iface = ifaces[i]; if (iface == null) continue; GatherInheritedInstanceInvariants(iface, consolidatedInvariants); } for (int i = 0; i < consolidatedInvariants.Count; i++){ Invariant inv = consolidatedInvariants[i]; stmts.Add(new If(new UnaryExpression(inv.Condition, NodeType.LogicalNot, SystemTypes.Boolean), new Block(new StatementList( new If(throwException, new Block(new StatementList( new Throw(new Construct(new MemberBinding(null, SystemTypes.ObjectInvariantException.GetConstructor()), null, SystemTypes.ObjectInvariantException), inv.SourceContext) )), new Block(new StatementList(new Return(Literal.False)))))), null)); } #endregion #region Create code for all of the modelfields defined by c. Add that code to stmts. StatementList mfStats = new StatementList(); ExpressionList modifiesList = new ExpressionList(); // synthesize modifies clause foreach (ModelfieldContract mfC in c.Contract.ModelfieldContracts) { //Add the following code to the method: // if (!E) { // <mfC.Modelfield> = value(mfC.Witness); // if (!E) throw exception; // } //where E is the conjunction of (1) all satisfies clauses of the contract, and (2) all satisfies clauses of overridden contracts in superclasses. //Note that satisifes clauses of contracts implemented by mfC (i.e., contracts in interfaces) have been copied to mfC. //Note that if f in C overrides f in D, and f in D overrides f in E, then f in C overrides f in E. Expression E = Literal.True; for (ModelfieldContract currentMfC = mfC; currentMfC != null; currentMfC = currentMfC.NearestOverriddenContract) { foreach (Expression satClause in currentMfC.SatisfiesList) { if (satClause == null) continue; //error will have been dealt with elsewhere E = new BinaryExpression(satClause, E, NodeType.LogicalAnd, SystemTypes.Boolean); } } Expression notE = new UnaryExpression(E, NodeType.LogicalNot, SystemTypes.Boolean); #region create the if statement //Start with the creation of the body of the if. MemberBinding lhs = new MemberBinding(new This(c), mfC.Modelfield); Statement setF = new AssignmentStatement(lhs, mfC.Witness); modifiesList.Add(lhs); // synthesize modifies clause String mfAsString = mfC.Modelfield.FullName; MemberBinding exc = new MemberBinding(null,SystemTypes.ModelfieldException.GetConstructor(SystemTypes.String)); Construct exception = new Construct(exc, new ExpressionList(new Literal(mfAsString,SystemTypes.String)), SystemTypes.ModelfieldException); Block innerIfBody = new Block(new StatementList( new If(throwException, new Block(new StatementList( new Throw(exception, mfC.Modelfield.SourceContext) )), new Block(new StatementList(new Return(Literal.False)))))); Statement innerIf = new If(notE, innerIfBody, null); StatementList body = new StatementList(); body.Add(setF); body.Add(innerIf); Statement outerIf = new If(notE, new Block(body), null); #endregion mfStats.Add(outerIf); } #endregion #region If c declares invariants or modelfields, then add a contract to c if it has none, and make sure that m is c's InvariantMethod. if (stmts.Count > 0 || mfStats.Count > 0) { Duplicator dup = new Duplicator(this.currentModule, this.currentType); dup.DuplicateFor[throwException.UniqueKey] = throwException; stmts = dup.VisitStatementList(stmts); mfStats = dup.VisitStatementList(mfStats); m.Body = new Block(stmts); m.Body.Statements.Add(new Block(mfStats)); //The model field code should be wrapped in a ContractMarkerException block, but I can't get it to work m.Body.Statements.Add(new Return(Literal.True)); m.Body.HasLocals = true; //who knows? there might be locals in the invariants or model fields (quantifier bound variables). #region Slap on NoDefaultContract and (what is roughly the equivalent of) a requires this.PreValid. //I doubt if this is still needed //No need for a runtime check of this precondition though, so directly add an attribute. //Bit of a hack, but there does not seem to be a really good place to do this. InstanceInitializer ndCtor = SystemTypes.NoDefaultContractAttribute.GetConstructor(); if (ndCtor != null) m.Attributes.Add(new AttributeNode(new MemberBinding(null, ndCtor), null, AttributeTargets.Method)); TypeNode guard = SystemTypes.Guard; if (guard != null) { Method method = guard.GetMethod(Identifier.For("FrameIsPrevalid"), SystemTypes.Object, SystemTypes.Type); if (method != null) { This t = new This(c); Expression req = new MethodCall( new MemberBinding(null, method), new ExpressionList(t, new UnaryExpression(new Literal(t.Type, SystemTypes.Type), NodeType.Typeof, OptionalModifier.For(SystemTypes.NonNullType, SystemTypes.Type)))); // Place it in the method contract so downstream tools that are in the compiler pipeline see it if (m.Contract == null) { m.Contract = new MethodContract(m); } if (m.Contract.Requires == null) { m.Contract.Requires = new RequiresList(1); } m.Contract.Requires.Add(new RequiresPlain(req)); m.Contract.Modifies = modifiesList; // needed for model fields // Since this happens after contracts are serialized, serialize the precondition and stick it in the method's attributes. ContractSerializer cs = new ContractSerializer(this.currentModule); cs.Visit(req); string val = cs.SerializedContract; InstanceInitializer ctor = SystemTypes.RequiresAttribute.GetConstructor(SystemTypes.String); MemberBinding attrBinding = new MemberBinding(null, ctor); ExpressionList args = new ExpressionList(); args.Add(new Literal(val, SystemTypes.String)); AttributeNode a = new AttributeNode(attrBinding, args, (AttributeTargets)0); m.Attributes.Add(a); if (modifiesList.Count > 0) { ctor = SystemTypes.ModifiesAttribute.GetConstructor(SystemTypes.String); for (int i = 0, n = modifiesList.Count; i < n; i++) { Expression e = modifiesList[i]; a = Checker.SerializeExpression(ctor, e, this.currentModule); m.Attributes.Add(a); } } } } #endregion if (c.Contract == null) { c.Contract = new TypeContract(c); c.Contract.DeclaringType = c; } if (c.Contract.InvariantMethod == null) { c.Contract.InvariantMethod = m; c.Members.Add(m); } //else assert (m == c.Contract.InvairantMethod) } #endregion }