public override void VisitEnsuresNormal(EnsuresNormal normal) { // Since we can copy contracts around now, we may already have the source text if it comes from a contract reference assembly if (normal.SourceConditionText != null) { return; } string source = GetSource("Contract.Ensures", ContextForTextExtraction(normal)); if (source == null) { if (writeOutput) { Console.WriteLine("<error generating documentation>"); } } else { normal.SourceConditionText = new Literal(source, SystemTypes.String); if (writeOutput) { Indent(); Console.WriteLine("{0}", source); } } }
public override EnsuresNormal VisitEnsuresNormal(EnsuresNormal normal) { var result = base.VisitEnsuresNormal(normal); result.UserMessage = (Expression)Visit(result.UserMessage); return(result); }
public override EnsuresNormal VisitEnsuresNormal(EnsuresNormal normal) { if (normal == null) { return(null); } var result = base.VisitEnsuresNormal(normal); result.UserMessage = ExtractorVisitor.FilterUserMessage(this.targetMethod, result.UserMessage); return(result); }
public override EnsuresNormal VisitEnsuresNormal(EnsuresNormal normal) { BlockExpression be = normal.PostCondition as BlockExpression; if (be != null && be.Type == SystemTypes.Void) // then it was put here by the extractor? { Expression e = DecompileBooleanExpression(be); Debug.Assert(e != null); return(new EnsuresNormal(e)); } else { return(normal); } }
// TODO: otherwise clauses on requires, exceptional ensures public virtual void SerializeMethodContract(MethodContract mc) { if (mc.Requires != null) { for (int i = 0, n = mc.Requires.Count; i < n; i++) { Requires r = mc.Requires[i]; if (r == null || r.Condition == null || r.Inherited) continue; ExpressionList el = SplitConjuncts(r.Condition); for (int j = 0, m = el.Count; j < m; j++) { Requires r_prime = new RequiresPlain(el[j]); InstanceInitializer ctor = SystemTypes.RequiresAttribute.GetConstructor(SystemTypes.String); AttributeNode a = Checker.SerializeExpression(ctor, r_prime.Condition, this.currentModule); mc.DeclaringMethod.Attributes.Add(a); } } } if (mc.Ensures != null) { for (int i = 0, n = mc.Ensures.Count; i < n; i++) { EnsuresExceptional ee = mc.Ensures[i] as EnsuresExceptional; if (ee != null) { if (ee.PostCondition == null) { if (ee.Inherited || ee.TypeExpression.SourceContext.Document == null) continue; // then it is "throws E;" InstanceInitializer ctor = SystemTypes.ThrowsAttribute.GetConstructor(SystemTypes.Type); AttributeNode a = Checker.SerializeExpressions(ctor, new ExpressionList(new Literal(ee.Type, SystemTypes.Type)), null, ee.TypeExpression.SourceContext, this.currentModule); mc.DeclaringMethod.Attributes.Add(a); } else { // then it is "throws E ensures Q;" or "throws (E e) ensures Q;" // don't split Q into its top-level conjuncts because duplicate throws clauses for the same exception type // are not allowed. if (ee.Inherited || ee.PostCondition.SourceContext.Document == null) continue; InstanceInitializer ctor = SystemTypes.ThrowsAttribute.GetConstructor(SystemTypes.Type, SystemTypes.String); AttributeNode a = Checker.SerializeExpressions(ctor, new ExpressionList(new Literal(ee.Type, SystemTypes.Type)), new ExpressionList(ee.PostCondition), ee.PostCondition.SourceContext, this.currentModule); mc.DeclaringMethod.Attributes.Add(a); } } else { Ensures e = mc.Ensures[i]; if (e == null || e.PostCondition == null || e.Inherited || e.PostCondition.SourceContext.Document == null) continue; ExpressionList el = SplitConjuncts(e.PostCondition); for (int j = 0, m = el.Count; j < m; j++) { EnsuresNormal e_prime = new EnsuresNormal(el[j]); InstanceInitializer ctor = SystemTypes.EnsuresAttribute.GetConstructor(SystemTypes.String); AttributeNode a = Checker.SerializeExpression(ctor, e_prime.PostCondition, this.currentModule); mc.DeclaringMethod.Attributes.Add(a); } } } } if (mc.Modifies != null && mc.Modifies.Count > 0) { for (int i = 0, n = mc.Modifies.Count; i < n; i++) { Expression e = mc.Modifies[i]; if (e == null || e.SourceContext.Document == null) continue; InstanceInitializer ctor = SystemTypes.ModifiesAttribute.GetConstructor(SystemTypes.String); AttributeNode a = Checker.SerializeExpression(ctor, e, this.currentModule); mc.DeclaringMethod.Attributes.Add(a); } } return; }
public virtual EnsuresNormal VisitEnsuresNormal(EnsuresNormal normal1, EnsuresNormal normal2) { if (normal1 == null) return null; if (normal2 == null) normal1.PostCondition = this.VisitExpression(normal1.PostCondition, null); else normal1.PostCondition = this.VisitExpression(normal1.PostCondition, normal2.PostCondition); return normal1; }
public override EnsuresNormal VisitEnsuresNormal(EnsuresNormal normal) { if (normal == null) return null; normal.PostCondition = this.VisitBooleanExpression(normal.PostCondition); return normal; }
public Method methodToInferPostFrom = null; //if set, Visit tries to infer a postcondition for method. public override StatementList VisitStatementList(StatementList statements) { if (statements == null) return null; foreach (Statement stat in statements) { Return r = stat as Return; if (r != null && r.Expression != null) { //Statement return E; found. Test admissibility of E as a witness. WitnessFromCodeAdmissibilityVisitor admis = new WitnessFromCodeAdmissibilityVisitor(); try { admis.VisitExpression(r.Expression); this.Witnesses.Add(r.Expression); //witness found, otherwise exception would have been thrown #region add inferred postconditions if needed TypeNode retType = (methodToInferPostFrom == null || methodToInferPostFrom.ReturnType == null) ? null : methodToInferPostFrom.ReturnType; if (r == statements[0] && retType != null && (retType.IsObjectReferenceType || retType.IsPrimitiveComparable)) { //the return statement is the first statement of the body. //We would like to add ensures result == r.Expression; However, we have to be careful, for 2 reasons. //1: == cannot be applied to structs and generic types. Note that we already tested that we do not return a struct. //2: result might be the implicitly boxed version of r.Expression (e.g., public object m() {returns 5;}), in which case result == r.Expression does not hold (in particular at runtime) //To account for 2, we have to box/unbox result and r.Expression as necessary //If r.Expression is a generic type, we have to distinguish cases !(r.Expression is System.ValueType) or (r.Expression is int) and act accordingly //(We do not (yet) support cases where r.Expression is a valueType other than an int, but they could be handled along the same lines.) if (methodToInferPostFrom.Contract == null) { methodToInferPostFrom.Contract = new MethodContract(methodToInferPostFrom); } SourceContext scInferred = methodToInferPostFrom.Name.SourceContext; //can't set the sourcetext, so error message will be confusing...but then again, the idea is that this can never crash //Duplicate the return expression to avoid sharing problems Duplicator dup = new Duplicator(this.methodToInferPostFrom.DeclaringType.DeclaringModule, this.methodToInferPostFrom.DeclaringType); Expression returnInEq = dup.VisitExpression(r.Expression); //needed for casting/boxing/unboxing MemberBinding intType = new MemberBinding(null, SystemTypes.Int32); Literal objectLit = new Literal(SystemTypes.Object, SystemTypes.Type); Literal intLit = new Literal(SystemTypes.Int32, SystemTypes.Type); Expression resultBinding = new ReturnValue(methodToInferPostFrom.ReturnType, scInferred); Expression resultInEq = resultBinding; if (r.Expression.NodeType == NodeType.Box) { //if the return expression has been (implicitly) boxed, unbox both it and result returnInEq = (returnInEq as BinaryExpression).Operand1; //as we have the return expression in hand, unboxing is easy //adjust resultInEq to (returnInEq.Type)resultInEq (i.e., add an unbox), using an explicit coercion BinaryExpression resultUnboxed = new BinaryExpression(resultInEq, intType, NodeType.Unbox, SystemTypes.Int32.GetReferenceType(), scInferred); //gets type System.Int32@ AddressDereference resultDeref = new AddressDereference(resultUnboxed, returnInEq.Type, scInferred); resultInEq = new BinaryExpression(resultDeref, intLit, NodeType.ExplicitCoercion, SystemTypes.Int32, scInferred); } if (returnInEq.Type != null && (returnInEq.Type.IsObjectReferenceType || returnInEq.Type.IsPrimitiveComparable)) { //returnInEq is not a user-defined struct (to which we can't apply ==) Expression eq = null; //holds the postcondition that is added if (!(isGenericTypeThatCanBeStruct(returnInEq.Type))) { //== can be applied to returnInEq. Therefore, it can also be applied to resultInEq, which is of a supertype (i.e., not generic type, not a struct). eq = new BinaryExpression(resultInEq, returnInEq, NodeType.Eq, SystemTypes.Boolean, scInferred); } else { #region adjust eq to compensate for generics //insert ensures !(returnInEq is System.ValueType) ==> (object)resultInEq == (object)returnInEq; //and ensures returnInEq is int ==> (int)(object)resultInEq == (int)(object)returnInEq; //the cast to object is needed to allow application of == and the cast to int. //Note that resultInEq.Type is known to also be a generic type as it is a supertype. //(and note that in this case, there was no unboxing) #region set up the antecedent !(returnInEq is System.ValueType) TypeNode theType = SystemTypes.ValueType; //antecedent: !(r.Expression is System.ValueType) Expression ante = new BinaryExpression(r.Expression, new Literal(theType, SystemTypes.Type), NodeType.Is, SystemTypes.Boolean, scInferred); ante = new UnaryExpression(ante, NodeType.LogicalNot, SystemTypes.Boolean, scInferred); #endregion #region adjust resultInEq and returnInEq to (object)resultInEq and (object)returnInEq //adjust resultInEq to (object)result, so that it can be used in == //that means Box it to T, then ExplicitCast it to Object. MemberBinding boxType = new MemberBinding(null, returnInEq.Type); //could also use method return type BinaryExpression resultBoxed = new BinaryExpression(resultBinding, boxType, NodeType.Box, returnInEq.Type, scInferred); resultInEq = new BinaryExpression(resultBoxed, objectLit, NodeType.ExplicitCoercion, SystemTypes.Object, scInferred); //adjust returnInEq to (object)returnInEq BinaryExpression returnBoxed = new BinaryExpression(returnInEq, boxType, NodeType.Box, returnInEq.Type, scInferred); returnInEq = new BinaryExpression(returnBoxed, objectLit, NodeType.ExplicitCoercion, SystemTypes.Object, scInferred); #endregion //Add first ensures; ensures ante ==> resultInEq == returnInEq; eq = new BinaryExpression(resultInEq, returnInEq, NodeType.Eq, SystemTypes.Boolean, scInferred); Expression impl = new BinaryExpression(ante, eq, NodeType.Implies, scInferred); EnsuresNormal firstPost = new EnsuresNormal(impl); firstPost.SourceContext = scInferred; methodToInferPostFrom.Contract.Ensures.Add(firstPost); //Now add ensures returnInEq is int ==> (int)(object)resultInEq == (int)(object)returnInEq; //antecedent: r.Expression is int Expression secondAnte = new BinaryExpression(r.Expression, intLit, NodeType.Is, SystemTypes.Boolean, scInferred); #region adjust resultInEq and returnInEq to (int)resultInEq and (int)returnInEq //set resultInSecondEq to (int)resultInEq, i.e., to (int)(object)result //this requires an unbox to int32@, then an adress-deref to int32, then an explicitcast to int32 //note that if we unbox to type Int32 directly, we get a "operation could destabilize the runtime" warining from the checkin tests BinaryExpression resultUnboxed = new BinaryExpression(resultInEq, intType, NodeType.Unbox, SystemTypes.Int32.GetReferenceType(), scInferred); //gets type System.Int32@ AddressDereference resultDeref = new AddressDereference(resultUnboxed, SystemTypes.Int32, scInferred); BinaryExpression resultInSecondEq = new BinaryExpression(resultDeref, intLit, NodeType.ExplicitCoercion, SystemTypes.Int32, scInferred); //adjust returnInEq to (int)returnInEq BinaryExpression returnUnboxed = new BinaryExpression(returnInEq, intType, NodeType.Unbox, SystemTypes.Int32.GetReferenceType(), scInferred); AddressDereference returnDeref = new AddressDereference(returnUnboxed, SystemTypes.Int32, scInferred); BinaryExpression returnInSecondEq = new BinaryExpression(returnDeref, intLit, NodeType.ExplicitCoercion, SystemTypes.Int32, scInferred); #endregion Expression secondEq = new BinaryExpression(resultInSecondEq, returnInSecondEq, NodeType.Eq, SystemTypes.Boolean, scInferred); eq = new BinaryExpression(secondAnte, secondEq, NodeType.Implies, SystemTypes.Boolean, scInferred); //(Ab)use eq to hold the implication #endregion } //Add the constructed equality as a postcondition. EnsuresNormal newPost = new EnsuresNormal(eq); newPost.SourceContext = scInferred; methodToInferPostFrom.Contract.Ensures.Add(newPost); } //else don't generate a postcondition, can't apply == to a user-defined struct } #endregion } catch (ApplicationException e) { ApplicationException dumme = e; } //witness not admissible ('using' e to avoid warning) } } return statements; }
public EventingVisitor(Action<EnsuresNormal> visitEnsuresNormal) { VisitedEnsuresNormal += visitEnsuresNormal; } public event Action<EnsuresNormal> VisitedEnsuresNormal; public override EnsuresNormal VisitEnsuresNormal(EnsuresNormal normal) { if (VisitedEnsuresNormal != null) VisitedEnsuresNormal(normal); return base.VisitEnsuresNormal(normal); }
public virtual void VisitEnsuresNormal(EnsuresNormal normal) { if (normal == null) return; this.VisitExpression(normal.PostCondition); }
private Ensures MakeEnsures(Expression expression) { makeRequires = false; var condition = (Expression) this.Visit(expression); var result = new EnsuresNormal(condition); result.SourceConditionText = conditionString; result.UserMessage = userMessage; return result; }
public override void VisitEnsuresNormal(EnsuresNormal normal) { this.checkingExceptionalPostcondition = false; base.VisitEnsuresNormal(normal); }
public override EnsuresNormal VisitEnsuresNormal(EnsuresNormal normal) { normal.PostCondition = this.VisitBooleanExpression(normal.PostCondition); return(normal); }
public override void VisitEnsuresNormal(EnsuresNormal normal) { seenDup = false; seenConstructArray = false; base.VisitEnsuresNormal(normal); }
private void ParseMethodContract(Method m, TokenSet followers, ref bool swallowedSemicolonAlready){ bool savedParsingStatement = this.parsingStatement; if (this.currentToken != Token.EndOfFile) this.parsingStatement = true; if (!swallowedSemicolonAlready) m.SourceContext.EndPos = this.scanner.endPos; MethodContract mc = new MethodContract(m); SourceContext initialSourceContext = this.scanner.CurrentSourceContext; while ( Parser.ContractStart[this.currentToken] ) { SourceContext ctx = this.scanner.CurrentSourceContext; Node n = null; int finalPos = 0; switch ( this.currentToken ) { case Token.Requires: { this.GetNextToken(); if (this.currentToken == Token.LeftBrace){ this.HandleError(Error.ExpectedExpression); break; // without this, the left bracket gets parsed as an anonymous nested function } Expression e = this.ParseExpression(followers|ContractStart|Token.Otherwise); if (mc.Requires == null) mc.Requires = new RequiresList(); if (this.currentToken != Token.Otherwise) { Requires r = new RequiresPlain(e); n = r; mc.Requires.Add(r); }else { this.Skip(Token.Otherwise); Expression e2 = this.ParseExpression(followers|ContractStart); Requires r = new RequiresOtherwise(e,e2); n = r; mc.Requires.Add(r); } finalPos = this.scanner.CurrentSourceContext.EndPos; swallowedSemicolonAlready= (this.currentToken == Token.Semicolon); this.SkipSemiColon(followers|ContractStart); break; } case Token.Modifies: { // modifies expressions have their source context set here within this // case, so don't use the variable "n" to hold on to the AST otherwise // it will have the wrong source context set for it at the end of the switch // statement n = null; this.insideModifiesClause = true; list : { this.GetNextToken(); // Token.Modifies or Token.Comma SourceContext sctx = this.scanner.CurrentSourceContext; Expression e = this.ParseExpression(followers | ContractStart | Token.Comma); if (mc.Modifies == null) mc.Modifies = new ExpressionList(); if (e != null) { // REVIEW: does this just silently miss errors? sctx.EndPos = e.SourceContext.EndPos; ModifiesClause modClause = e as ModifiesClause; if (modClause != null) { e.SourceContext = sctx; } else { e = new UnaryExpression(e, NodeType.RefAddress, sctx); } mc.Modifies.Add(e); } if (this.currentToken == Token.Comma) goto list; } swallowedSemicolonAlready= (this.currentToken == Token.Semicolon); finalPos = this.scanner.CurrentSourceContext.EndPos; this.SkipSemiColon(followers|ContractStart); this.insideModifiesClause = false; break; } case Token.Ensures: { InEnsuresContext = true; this.GetNextToken(); if (this.currentToken == Token.LeftBrace){ this.HandleError(Error.ExpectedExpression); break; // without this, the left bracket gets parsed as an anonymous nested function } Expression e = this.ParseExpression(followers|ContractStart); if (mc.Ensures == null) mc.Ensures = new EnsuresList(); EnsuresNormal en = new EnsuresNormal(e); n = en; mc.Ensures.Add(en); finalPos = this.scanner.CurrentSourceContext.EndPos; swallowedSemicolonAlready= (this.currentToken == Token.Semicolon); this.SkipSemiColon(followers|ContractStart); InEnsuresContext = false; break; } case Token.Throws: { this.GetNextToken(); // throws (E1) ensures P; // throws (E1 e) ensures P; // throws E1 ensures P; // throws E1, E2, ...; // Note, for constuctors, only the last of these forms is allowed. if (mc.Ensures == null) { mc.Ensures = new EnsuresList(); // Note, this list may be left empty in case of parsing errors below. } EnsuresExceptional exc = new EnsuresExceptional(); exc.SourceContext = this.scanner.CurrentSourceContext; bool hasLeftParen = false; if (this.currentToken == Token.LeftParenthesis) { hasLeftParen = true; this.Skip(Token.LeftParenthesis); } exc.Type = exc.TypeExpression = this.ParseTypeExpression(Identifier.Empty, followers|Token.Identifier|Token.RightParenthesis|ContractStart); if (hasLeftParen && Parser.IdentifierOrNonReservedKeyword[this.currentToken]) { exc.Variable = this.scanner.GetIdentifier(); exc.Variable.Type = exc.Type; this.GetNextToken(); }else{ exc.Variable = null; // need to be able to distinguish whether the source contains a variable or not } if (hasLeftParen) { this.Skip(Token.RightParenthesis); } if (hasLeftParen || this.currentToken == Token.Ensures) { // throws (E1) ensures P; // throws (E1 e) ensures P; // throws E1 ensures P; SourceContext ctxEnsures = this.scanner.CurrentSourceContext; this.Skip(Token.Ensures); InEnsuresContext = true; Expression ens = this.ParseExpression(followers|ContractStart); InEnsuresContext = false; // Do the constructor check now. This is rather late, since the whole throws...ensures // has now been parsed, but this may lead to better parse-error recovery. if (m is InstanceInitializer) { this.HandleError(ctxEnsures, Error.ThrowsEnsuresOnConstructor); // ignore what was parsed exc.PostCondition = new Literal(true, null, ctx); }else{ exc.PostCondition = ens; } mc.Ensures.Add(exc); }else{ // throws E1, E2, ...; // exc.PostCondition = new Literal(true, null, ctx); mc.Ensures.Add(exc); while (this.currentToken == Token.Comma) { this.GetNextToken(); exc = new EnsuresExceptional(); exc.SourceContext = this.scanner.CurrentSourceContext; exc.Type = exc.TypeExpression = this.ParseTypeExpression(Identifier.Empty, followers|Token.Comma|ContractStart); exc.Variable = new Local(TypeExpressionFor("System", "Exception")); exc.Variable.SourceContext = ctx; exc.PostCondition = new Literal(true, null, ctx); mc.Ensures.Add(exc); } } finalPos = this.scanner.CurrentSourceContext.EndPos; swallowedSemicolonAlready= (this.currentToken == Token.Semicolon); this.SkipSemiColon(followers|ContractStart); n = exc; break; } } if (n != null) { n.SourceContext= ctx; n.SourceContext.EndPos = finalPos ; } m.SourceContext.EndPos = finalPos; } // What error to generate here? if (!followers[this.currentToken]) this.SkipTo(followers); if (initialSourceContext.EndPos != this.scanner.CurrentSourceContext.EndPos) { // then a contract really was parsed m.Contract = mc; } if (this.currentToken != Token.EndOfFile) this.parsingStatement = savedParsingStatement; }
public override EnsuresNormal VisitEnsuresNormal(EnsuresNormal normal) { if (normal == null) return null; { #region Generate the runtime code for checking the postcondition Expression en = normal.PostCondition; if (en == null) return normal; SourceContext sc = en.SourceContext; // Code generation for postconditions needs to be such that the // data flow analysis will "see" the consequences. If the value // of the postcondition is assigned to a local, then the information // is lost. // // try { // if en goto post_holds; // } // catch { throw new ErrorDuringPostConditionEvaluation(...); } // throw new PostConditionException(...); // post_holds: nop Block postConditionBlock = new Block(new StatementList()); #region Evaluate the postcondition within a try block. If an exception happens during evaluation, throw a wrapped exception. bool noAllocationAllowed = this.currentMethod.GetAttribute(SystemTypes.BartokNoHeapAllocationAttribute) != null; Local exceptionDuringPostCondition = new Local(Identifier.For("SS$exceptionDuringPostCondition"),SystemTypes.Exception); Local objectExceptionDuringPostCondition = new Local(Identifier.For("SS$objectExceptionDuringPostCondition"),SystemTypes.Object); Expression cond = normal.PostCondition; string condition = cond != null && cond.SourceContext.SourceText != null && cond.SourceContext.SourceText.Length > 0 ? cond.SourceContext.SourceText : "<unknown condition>"; Expression ec2; Expression ec3; if (noAllocationAllowed) { ec2 = new MemberBinding(null, SystemTypes.PreAllocatedExceptions.GetField(Identifier.For("InvalidContract"))); ec3 = new MemberBinding(null, SystemTypes.PreAllocatedExceptions.GetField(Identifier.For("InvalidContract"))); } else { MemberBinding excBinding2 = new MemberBinding(null, SystemTypes.InvalidContractException.GetConstructor(SystemTypes.String, SystemTypes.Exception)); MemberBinding excBinding3 = new MemberBinding(null, SystemTypes.InvalidContractException.GetConstructor(SystemTypes.String)); string msg2 = "Exception occurred during evaluation of postcondition '" + condition + "' in method '" + currentMethod.FullName + "'"; ec2 = new Construct(excBinding2, new ExpressionList(new Literal(msg2, SystemTypes.String), exceptionDuringPostCondition)); ec3 = new Construct(excBinding3, new ExpressionList(new Literal(msg2, SystemTypes.String))); } #endregion #region Throw an exception if the value of the postcondition was false Block post_holds = new Block(new StatementList(new Statement(NodeType.Nop))); Expression thrownException; if (noAllocationAllowed) { thrownException = new MemberBinding(null, SystemTypes.PreAllocatedExceptions.GetField(Identifier.For("Ensures"))); } else { MemberBinding excBinding = new MemberBinding(null, SystemTypes.EnsuresException.GetConstructor(SystemTypes.String)); Construct ec = new Construct(excBinding, new ExpressionList()); string msg = "Postcondition '" + condition + "' violated from method '" + currentMethod.FullName + "'"; ec.Operands.Add(new Literal(msg, SystemTypes.String)); thrownException = ec; } Throw t = new Throw(thrownException,sc); postConditionBlock.Statements.Add(new If(en,new Block(new StatementList(new Branch(null,post_holds))), null)); postConditionBlock.Statements.Add(t); postConditionBlock.Statements.Add(post_holds); #endregion postConditionBlock = this.VisitBlock(postConditionBlock); this.currentContractNormalTerminationCheck.Statements.Add(postConditionBlock); #endregion return normal; } }
public override EnsuresNormal VisitEnsuresNormal(EnsuresNormal normal) { var result = base.VisitEnsuresNormal(normal); result.UserMessage = (Expression) Visit(result.UserMessage); return result; }
public virtual EnsuresNormal VisitEnsuresNormal(EnsuresNormal normal) { if (normal == null) return null; normal.PostCondition = this.VisitExpression(normal.PostCondition); return normal; }
public override EnsuresNormal VisitEnsuresNormal(EnsuresNormal normal) { if (normal == null) return null; return base.VisitEnsuresNormal((EnsuresNormal)normal.Clone()); }
private bool ExtractFromClump(StatementList contractClump, Method method, GatherLocals gatherLocals, RequiresList Preconditions, EnsuresList Postconditions, RequiresList validations, EnsuresList modelPostconditions, SourceContext defaultContext, Method originalMethod, Block contractInitializer, ref HelperMethods.StackDepthTracker dupStackTracker) { // set the state so that the contract clump is used for extraction (as opposed to the method body as it used to) StatementList stmts = contractClump; int beginning = 0; int n = stmts.Count; int seginning = HelperMethods.FindNextRealStatement(((Block) stmts[beginning]).Statements, 0); bool endContractFound = false; bool postConditionFound = false; SourceContext currentSourceContext; for (int i = beginning; i < n; i++) { Block b = (Block) stmts[i]; if (b == null) continue; for (int j = 0, m = b.Statements == null ? 0 : b.Statements.Count; j < m; j++) { if (dupStackTracker.IsValid && dupStackTracker.Depth >= 0) { b.Statements[j] = dupStackTracker.Visit(b.Statements[j]); } Statement s = b.Statements[j]; if (s == null) continue; Block currentClump; Throw t = null; t = s as Throw; Method calledMethod = HelperMethods.IsMethodCall(s); if ((t != null || (calledMethod != null && calledMethod.DeclaringType != null && calledMethod.DeclaringType != this.contractNodes.ContractClass && HelperMethods.IsVoidType(calledMethod.ReturnType) && !this.contractNodes.IsContractOrValidatorOrAbbreviatorMethod(calledMethod)))) { // Treat throw statements as (part of) a precondition // don't accept "throw ..." unless it comes in the "precondition section" // then treat the current clump as a precondition, but need to massage it a bit: // all branches to the block just after the throw should be modified to be branches to // a new manufactured block that sets a fresh local to "true". The // throw itself should be changed to set the same local to "false". That way the // clump can be treated as the value of precondition (because the branch polarity has // already been negated as part of the code gen). // This test was supposed to be a sanity check that the current block contained // only "throw ..." or else "nop; throw ...". But I've also seen "ThrowHelper.Throw(...); nop", // so I'm just going to comment this out for now. //if (!((m == 1 && j == 0) || (m == 2 && j == 1))) { // Preconditions = new RequiresList(); // Postconditions = new EnsuresList(); // return; // throw new ExtractorException(); //} Expression exception; // The clump being extracted may contain code/blocks that represent (part of) // the expression that is being thrown (if the original throw expression had // control flow in it from boolean expressions and/or ternary expressions). b.Statements[j] = null; // wipe out throw statement currentClump = new Block(HelperMethods.ExtractClump(stmts, beginning, seginning, i, j)); int currentClumpLength = i - beginning + 1; // there better be a next block because that must have been the target for all of the branches // that didn't cause the throw to happen if (!(i < n - 1)) { this.HandleError(method, 1027, "Malformed contract.", s.SourceContext); return false; } Block nextBlock = (Block) stmts[i + 1]; // cast succeeds because body is clump Local valueOfPrecondition = new Local(Identifier.For("_preConditionHolds"), SystemTypes.Boolean); Block preconditionHolds = new Block(new StatementList(new AssignmentStatement(valueOfPrecondition, Literal.True))); ReplaceBranchTarget rbt = new ReplaceBranchTarget(nextBlock, preconditionHolds); rbt.VisitBlock(currentClump); int ILOffset; CountPopExpressions cpe = new CountPopExpressions(); currentSourceContext = s.SourceContext; cpe.Visit(s); if (0 < cpe.PopOccurrences) { // then there is a set of blocks that represent the exception: the Reader // was not able to decompile it back into an expression. Extract the set // from the current clump and make it into a block expression // Find the last block that has a branch to "preconditionHolds". After that are all of the blocks // that represent the evaluation of the exception int branchBlockIndex = currentClumpLength - 2; // can't be the current block: that has the throw in it while (0 <= branchBlockIndex) { Block possibleBranchBlock = currentClump.Statements[branchBlockIndex] as Block; Branch br = possibleBranchBlock.Statements[possibleBranchBlock.Statements.Count - 1] as Branch; if (br != null && br.Target == preconditionHolds) { break; } branchBlockIndex--; } if (branchBlockIndex < 0) { this.HandleError(method, 1028, "Malformed exception constructor in contract.", defaultContext); return false; } Block exceptionBlock = new Block(HelperMethods.ExtractClump(currentClump.Statements, branchBlockIndex + 1, 0, currentClumpLength - 1, ((Block) currentClump.Statements[currentClumpLength - 1]).Statements.Count - 1)); exceptionBlock.Statements.Add(new ExpressionStatement(t.Expression)); SourceContext sctx = ((Block) exceptionBlock.Statements[0]).Statements[0].SourceContext; if (sctx.IsValid) { currentSourceContext = sctx; } else { SourceContext tmp; bool foundContext = HelperMethods.GetLastSourceContext(exceptionBlock.Statements, out tmp); if (foundContext) currentSourceContext = tmp; } if (!CheckClump(method, gatherLocals, currentSourceContext, exceptionBlock)) return false; exception = new BlockExpression(exceptionBlock, SystemTypes.Exception); ILOffset = t.ILOffset; } else { currentSourceContext = s.SourceContext; if (t != null) { // then the statement is "throw ..." exception = t.Expression; ILOffset = t.ILOffset; } else { ExpressionStatement throwHelperCall = s as ExpressionStatement; Debug.Assert(throwHelperCall != null); exception = throwHelperCall.Expression; ILOffset = s.ILOffset; } exception.SourceContext = currentSourceContext; SourceContext tmp; bool foundContext = HelperMethods.GetLastSourceContext(currentClump.Statements, out tmp); if (foundContext) currentSourceContext = tmp; } Block returnValueOfPrecondition = new Block(new StatementList(new ExpressionStatement(valueOfPrecondition))); Statement extraAssumeFalse = this.ExtraAssumeFalseOnThrow(); Block preconditionFails = new Block(new StatementList(new AssignmentStatement(valueOfPrecondition, Literal.False), extraAssumeFalse, new Branch(null, returnValueOfPrecondition, true, false, false))); //Block preconditionFails = new Block(new StatementList(new AssignmentStatement(valueOfPrecondition, Literal.False), new Branch(null, returnValueOfPrecondition, true, false, false))); currentClump.Statements.Add(preconditionFails); // replace throw statement currentClump.Statements.Add(preconditionHolds); currentClump.Statements.Add(returnValueOfPrecondition); if (!CheckClump(originalMethod, gatherLocals, currentSourceContext, currentClump)) return false; BlockExpression be = new BlockExpression(currentClump, SystemTypes.Boolean); be.SourceContext = currentSourceContext; var ro = new RequiresOtherwise(be, exception); ro.ILOffset = ILOffset; ro.SourceContext = currentSourceContext; if (postConditionFound) { HandleError(originalMethod, 1013, "Precondition found after postcondition.", currentSourceContext); return false; } validations.Add(ro); var req = new RequiresPlain(be, FindExceptionThrown.Find(exception)); req.IsFromValidation = true; req.ILOffset = ro.ILOffset; req.SourceContext = ro.SourceContext; Preconditions.Add(req); } else { if (contractNodes.IsContractMethod(calledMethod)) { // Treat calls to contract methods if (endContractFound) { HandleError(originalMethod, 1012, "Contract call found after prior EndContractBlock.", s.SourceContext); break; } if (contractNodes.IsEndContract(calledMethod)) { endContractFound = true; continue; } MethodCall mc = ((ExpressionStatement) s).Expression as MethodCall; Expression arg = mc.Operands[0]; arg.SourceContext = s.SourceContext; MethodContractElement mce; currentSourceContext = s.SourceContext; Expression condition; if (beginning == i && seginning == j) { // Deal with the simple case: the reader decompiled the call into a single statement condition = arg; } else { b.Statements[j] = new ExpressionStatement(arg); // construct a clump from // methodBody.Statements[beginning].Statements[seginning] to // methodBody.Statements[i].Statements[j] currentClump = new Block(HelperMethods.ExtractClump(stmts, beginning, seginning, i, j)); if (!currentSourceContext.IsValid) { // then a good source context has not been found yet. Grovel around in the clump // to see if there is a better one SourceContext sctx; if (HelperMethods.FindContext(currentClump, currentSourceContext, out sctx)) currentSourceContext = sctx; } if (!CheckClump(originalMethod, gatherLocals, currentSourceContext, currentClump)) return false; BlockExpression be = new BlockExpression(currentClump); condition = be; } condition.SourceContext = currentSourceContext; if (contractNodes.IsPlainPrecondition(calledMethod)) { var req = new RequiresPlain(condition); contractNodes.IsRequiresWithException(calledMethod, out req.ExceptionType); mce = req; } else if (this.contractNodes.IsPostcondition(calledMethod)) { mce = new EnsuresNormal(condition); } else if (contractNodes.IsExceptionalPostcondition(calledMethod)) { EnsuresExceptional ee = new EnsuresExceptional(condition); // Extract the type of exception. ee.Type = calledMethod.TemplateArguments[0]; mce = ee; } else { throw new InvalidOperationException("Cannot recognize contract method"); } mce.SourceContext = currentSourceContext; mce.ILOffset = mc.ILOffset; if (1 < mc.Operands.Count) { var candidate = SanitizeUserMessage(method, mc.Operands[1], currentSourceContext); mce.UserMessage = candidate; } if (2 < mc.Operands.Count) { Literal lit = mc.Operands[2] as Literal; if (lit != null) { mce.SourceConditionText = lit; } } // determine Model status mce.UsesModels = CodeInspector.UsesModel(mce.Assertion, this.contractNodes); // Check context rules switch (mce.NodeType) { case NodeType.RequiresPlain: if (postConditionFound) { this.HandleError(originalMethod, 1014, "Precondition found after postcondition.", currentSourceContext); return false; } if (mce.UsesModels) { this.HandleError(originalMethod, 1073, "Preconditions may not refer to model members.", currentSourceContext); return false; } var rp = (RequiresPlain) mce; Preconditions.Add(rp); validations.Add(rp); // also add to the internal validation list break; // TODO: check visibility of post conditions based on visibility of possible implementation case NodeType.EnsuresNormal: case NodeType.EnsuresExceptional: Ensures ensures = (Ensures) mce; if (mce.UsesModels) { if (this.IncludeModels) { modelPostconditions.Add(ensures); } } else { Postconditions.Add(ensures); } postConditionFound = true; break; } } else if (ContractNodes.IsValidatorMethod(calledMethod)) { // Treat calls to Contract validators if (endContractFound) { this.HandleError(originalMethod, 1012, "Contract call found after prior EndContractBlock.", s.SourceContext); break; } MethodCall mc = ((ExpressionStatement) s).Expression as MethodCall; var memberBinding = (MemberBinding) mc.Callee; currentSourceContext = s.SourceContext; Statement validation; Block validationPrefix; if (beginning == i && seginning == j) { // Deal with the simple case: the reader decompiled the call into a single statement validation = s; validationPrefix = null; } else { // The clump may contain multiple statements ending in the validator call. // to extract the code as Requires<E>, we need to keep the statements preceeding // the validator call, as they may contain local initialization etc. These should go // into the first Requires<E> that the validator expands to. This way, if there are // no Requires<E> expanded from the validator, then the statements can be omitted. // At the same time, the statements won't be duplicated when validations are emitted. // // If the validator call contains any pops, then the extraction must fail saying it // is too complicated. // must null out statement with call before extract clump b.Statements[j] = null; // we have a copy in mc, s validationPrefix = new Block(HelperMethods.ExtractClump(stmts, beginning, seginning, i, j)); if (!currentSourceContext.IsValid) { // then a good source context has not been found yet. Grovel around in the clump // to see if there is a better one SourceContext sctx; if (HelperMethods.FindContext(validationPrefix, currentSourceContext, out sctx)) currentSourceContext = sctx; } if (CountPopExpressions.Count(mc) > 0) { this.HandleError(method, 1071, "Arguments to contract validator call are too complicated. Please simplify.", currentSourceContext); return false; } if (!CheckClump(originalMethod, gatherLocals, currentSourceContext, validationPrefix)) return false; validation = new Block(new StatementList(validationPrefix, s)); validation.SourceContext = currentSourceContext; } var ro = new RequiresOtherwise(null, new BlockExpression(new Block(new StatementList(validation)))); validations.Add(ro); CopyValidatorContracts( method, calledMethod, memberBinding.TargetObject, mc.Operands, Preconditions, currentSourceContext, validationPrefix); } else if (ContractNodes.IsAbbreviatorMethod(calledMethod)) { // Treat calls to Contract abbreviators if (endContractFound) { this.HandleError(originalMethod, 1012, "Contract call found after prior EndContractBlock.", s.SourceContext); break; } MethodCall mc = ((ExpressionStatement) s).Expression as MethodCall; var memberBinding = (MemberBinding) mc.Callee; currentSourceContext = s.SourceContext; if (beginning == i && seginning == j) { // Deal with the simple case: the reader decompiled the call into a single statement // nothing to do. All is in the call and its arguments } else { // The clump may contain multiple statements ending in the abbreviator call. // We need to keep the statements preceeding the abbreviator call and add them to the // contract initializer block. The reason we cannot add them to the first expansion contract // of the abbreviator is that the abbreviator may give rise to closure initialization which will // be hoisted into the closure initializer block. This closure initializer may refer to the // locals initialized by the present statement sequence, so it must precede it. // // If the abbreviator call contains any pops, then the extraction must fail saying it // is too complicated. // grab prefix of clump minus last call statement. // must null out current call statement before we extract clump (ow. it stays in body) b.Statements[j] = null; currentClump = new Block(HelperMethods.ExtractClump(stmts, beginning, seginning, i, j)); if (!currentSourceContext.IsValid) { // then a good source context has not been found yet. Grovel around in the clump // to see if there is a better one SourceContext sctx; if (HelperMethods.FindContext(currentClump, currentSourceContext, out sctx)) currentSourceContext = sctx; } if (CountPopExpressions.Count(mc) > 0) { this.HandleError(method, 1070, "Arguments to contract abbreviator call are too complicated. Please simplify.", currentSourceContext); return false; } if (!CheckClump(originalMethod, gatherLocals, currentSourceContext, currentClump)) return false; if (HelperMethods.IsNonTrivial(currentClump)) { contractInitializer.Statements.Add(currentClump); } } CopyAbbreviatorContracts(method, calledMethod, memberBinding.TargetObject, mc.Operands, Preconditions, Postconditions, currentSourceContext, validations, contractInitializer); } else { // important to continue here and accumulate blocks/statements for next contract! if (i == beginning && j == seginning && s.NodeType == NodeType.Nop) { // nop following contract is often associated with previous code, so skip it seginning = j + 1; } continue; } } // Re-initialize current state after contract has been found beginning = i; seginning = j + 1; //seginning = HelperMethods.FindNextRealStatement(((Block)stmts[i]).Statements, j + 1); if (seginning < 0) seginning = 0; //b = (Block)stmts[i]; // IMPORTANT! Need this to keep "b" in sync } } if (this.verbose) { Console.WriteLine("\tNumber of Preconditions: " + Preconditions.Count); Console.WriteLine("\tNumber of Postconditions: " + Postconditions.Count); } return true; }
public override EnsuresNormal VisitEnsuresNormal(EnsuresNormal normal) { if (normal == null) return null; var result = base.VisitEnsuresNormal(normal); result.UserMessage = ExtractorVisitor.FilterUserMessage(this.targetMethod, result.UserMessage); return result; }
public override void VisitEnsuresNormal(EnsuresNormal normal) { seenDup = false; base.VisitEnsuresNormal(normal); }
public override EnsuresNormal VisitEnsuresNormal(EnsuresNormal normal) { string str=CodePrinter.NodeToString(normal.PostCondition); ensures.Add(str); return normal; }