/// <summary> /// Accumulates all elements from <paramref name="sourceContract"/> into <paramref name="targetContract"/> /// </summary> /// <param name="targetContract">Contract which is target of accumulator</param> /// <param name="sourceContract">Contract which is source of accumulator</param> public static void AddMethodContract(MethodContract targetContract, IMethodContract sourceContract) { targetContract.Locations.AddRange(sourceContract.Locations); targetContract.Preconditions.AddRange(sourceContract.Preconditions); targetContract.Postconditions.AddRange(sourceContract.Postconditions); targetContract.ThrownExceptions.AddRange(sourceContract.ThrownExceptions); targetContract.IsPure |= sourceContract.IsPure; // need the disjunction return; }
public override void TraverseChildren(IMethodDefinition method) { if (!MemberHelper.IsVisibleOutsideAssembly(method)) { return; } var returnType = method.Type; if (returnType == this.host.PlatformType.SystemVoid || returnType.IsEnum || returnType.IsValueType ) { return; } var newContract = new Microsoft.Cci.MutableContracts.MethodContract(); var post = new List <IPostcondition>(); var p = new Microsoft.Cci.MutableContracts.Postcondition() { Condition = new NotEquality() { LeftOperand = new ReturnValue() { Type = returnType, }, RightOperand = new CompileTimeConstant() { Type = returnType, Value = null, }, Type = this.host.PlatformType.SystemBoolean, }, OriginalSource = "result != null", }; post.Add(p); newContract.Postconditions = post; var contract = this.contractProvider.GetMethodContractFor(method); if (contract != null) { Microsoft.Cci.MutableContracts.ContractHelper.AddMethodContract(newContract, contract); } this.contractProvider.AssociateMethodWithContract(method, newContract); base.TraverseChildren(method); }
/// <summary> /// Mutates the <paramref name="methodContract"/> by removing any contracts that violate any of the rules /// about contracts, e.g., preconditions mentioning a member that is more restrictive than the method containing /// the precondition. /// TODO: Return a list of errors. /// </summary> public static void CheckMethodContract(IMetadataHost host, IMethodDefinition method, MethodContract methodContract) { var reqs = methodContract.Preconditions; var newReqs = new List<IPrecondition>(methodContract.Preconditions.Count); foreach (var p in reqs) { var contractExpression = p.Condition; var v = Visibility.MostRestrictiveVisibility(host, contractExpression); var currentVisibility = method.Visibility; var intersection = TypeHelper.VisibilityIntersection(v, currentVisibility); if (intersection == currentVisibility) { newReqs.Add(p); } else { // TODO!! BUGBUG!! Need to signal an error, not just silently not add the precondition! } } methodContract.Preconditions = newReqs; }
public override void TraverseChildren(IMethodDefinition method) { // inject only in visible method if(!MemberHelper.IsVisibleOutsideAssembly(method)) return; // inject only in methods that return a reference type var returnType = method.Type; if(returnType == this.host.PlatformType.SystemVoid || returnType.IsValueType) return; // create new postcondition // Contract.Ensures(Contract.Result<T>() != null); var newContract = new MethodContract { Postconditions = new List<IPostcondition> { new Postcondition { // != Condition = new NotEquality { // result LeftOperand = new ReturnValue { Type = returnType, }, // null RightOperand = new CompileTimeConstant { Type = returnType, Value = null, }, Type = this.host.PlatformType.SystemBoolean, }, // description OriginalSource = "result != null", } } }; // merge existing contracts, if any var contract = this.contractProvider.GetMethodContractFor(method); if(contract != null) { ContractHelper.AddMethodContract(newContract,contract); } // store new contracts this.host.Event(CcsEventLevel.Message,"ensures not null: {0}",method); this.contractProvider.AssociateMethodWithContract(method,newContract); }
/// <summary> /// Visits the specified method contract. /// </summary> /// <param name="methodContract">The method contract.</param> /// <returns></returns> protected virtual IMethodContract DeepCopy(MethodContract methodContract) { methodContract.Allocates = this.DeepCopy(methodContract.Allocates); methodContract.Frees = this.DeepCopy(methodContract.Frees); methodContract.ModifiedVariables = this.DeepCopy(methodContract.ModifiedVariables); methodContract.Postconditions = this.DeepCopy(methodContract.Postconditions); methodContract.Preconditions = this.DeepCopy(methodContract.Preconditions); methodContract.Reads = this.DeepCopy(methodContract.Reads); methodContract.ThrownExceptions = this.DeepCopy(methodContract.ThrownExceptions); methodContract.Writes = this.DeepCopy(methodContract.Writes); return methodContract; }
/// <summary> /// Returns the method contract, if any, that has been associated with the given object. Returns null if no association exits. /// </summary> /// <param name="method">An object that might have been associated with a method contract. This can be any kind of object.</param> /// <returns></returns> /*?*/ public IMethodContract GetMethodContractFor(object method) { if (this.methodsBeingExtracted.Contains(method)) { // hit a cycle while chasing validators/abbreviators // TODO: signal error return null; } else { this.methodsBeingExtracted.Add(method); } try { IMethodContract contract = this.underlyingContractProvider.GetMethodContractFor(method); if (contract != null) return contract == ContractDummy.MethodContract ? null : contract; MethodContract result = new MethodContract(); IMethodContract primaryContract = this.primaryExtractor.GetMethodContractFor(method); bool found = false; if (primaryContract != null) { found = true; Microsoft.Cci.MutableContracts.ContractHelper.AddMethodContract(result, primaryContract); } if (this.oobExtractors != null) { foreach (var oobProvider in this.oobExtractors) { IMethodReference methodReference = method as IMethodReference; if (methodReference == null) continue; // REVIEW: Is there anything else it could be and still find a contract for it? MappingMutator primaryToOobMapper = this.mapperForPrimaryToOob[oobProvider]; var oobMethod = primaryToOobMapper.Map(methodReference); if (oobMethod == null) continue; var oobContract = oobProvider.GetMethodContractFor(oobMethod); if (oobContract == null) continue; MappingMutator oobToPrimaryMapper = this.mapperForOobToPrimary[oobProvider]; oobContract = oobToPrimaryMapper.Map(oobContract); var sps = new Microsoft.Cci.MutableContracts.SubstituteParameters(this.host, oobMethod.ResolvedMethod, methodReference.ResolvedMethod); oobContract = sps.Visit(oobContract); Microsoft.Cci.MutableContracts.ContractHelper.AddMethodContract(result, oobContract); found = true; } } // always cache so we don't try to extract more than once if (found) { this.underlyingContractProvider.AssociateMethodWithContract(method, result); } else { this.underlyingContractProvider.AssociateMethodWithContract(method, ContractDummy.MethodContract); result = null; } return result; } finally { this.methodsBeingExtracted.RemoveAt(this.methodsBeingExtracted.Count - 1); } }
/// <summary> /// Adds the rsd contract to the method, it is added as a postcondition where the rsd field of the type is less than or equal to limit /// </summary> protected FieldDefinition AddRsdContractToMethod(IMethodDefinition method, ITypeReference type, string rsdName, IExpression limit, IExpression cond) { var rsdField = GetRsdField(method, rsdName, type); var newContract = new Microsoft.Cci.MutableContracts.MethodContract(); var postconditions = new List<IPostcondition>(); var limitCondition = new LessThanOrEqual() { LeftOperand = new BoundExpression() { Definition = rsdField, Type = rsdField.Type }, RightOperand = limit, Type = _host.PlatformType.SystemBoolean, }; var int32Type = new PlatformType(_host).SystemInt32; if (cond != null) { //cond ==> tmp <= limit =~= //!cond || tmp <= limit =~= //!cond ? tmp <= limit : true postconditions.Add( new Microsoft.Cci.MutableContracts.PostCondition() { Condition = new Conditional() { Condition = new LogicalNot() { Operand = cond }, Type = int32Type, ResultIfTrue = new CompileTimeConstant() { Type = int32Type, Value = 1 }, ResultIfFalse = limitCondition }, OriginalSource = "!cond ? rsd <= limit : true", } ); } else { postconditions.Add( new Microsoft.Cci.MutableContracts.PostCondition() { Condition = limitCondition, OriginalSource = "rsd <= limit", } ); } newContract.Postconditions = postconditions; var contract = _contractProvider.GetMethodContractFor(method); if (contract != null) { Microsoft.Cci.MutableContracts.ContractHelper.AddMethodContract(newContract, contract); } _contractProvider.AssociateMethodWithContract(method, newContract); return rsdField; }
private static MethodContract FilterUserMessageAndLegacyPreconditions(IContractAwareHost host, IMethodContract contract, ITypeDefinition typeDefinition, bool keepLegacy) { var mc = new MethodContract(contract); mc.Postconditions = FilterUserMessage(host, contract.Postconditions, typeDefinition); mc.Preconditions = FilterUserMessageAndLegacyPreconditions(host, contract.Preconditions, typeDefinition, keepLegacy); mc.ThrownExceptions = FilterUserMessage(host, contract.ThrownExceptions, typeDefinition); return mc; }
/// <summary> /// Returns a method contract containing the 'effective' contract for the given /// method definition. The effective contract contains all contracts for the method: /// any that it has on its own, as well as all those inherited from any methods /// that it overrides or interface methods that it implements (either implicitly /// or explicitly). /// All parameters in inherited contracts are substituted for by /// the method's own parameters. /// If there are no contracts, then it returns null. /// Any preconditions that were written as legacy-preconditions or calls to Requires<E> are /// included iff <paramref name="keepLegacyPreconditions"/>. /// </summary> public static IMethodContract GetMethodContractForIncludingInheritedContracts(IContractAwareHost host, IMethodDefinition methodDefinition, bool keepLegacyPreconditions = true) { MethodContract cumulativeContract = new MethodContract(); bool atLeastOneContract = false; IMethodContract/*?*/ mc = ContractHelper.GetMethodContractFor(host, methodDefinition); if (mc != null) { Microsoft.Cci.MutableContracts.ContractHelper.AddMethodContract(cumulativeContract, mc); atLeastOneContract = true; } #region Overrides of base class methods if (!methodDefinition.IsNewSlot) { // REVIEW: Is there a better test? IMethodDefinition overriddenMethod = MemberHelper.GetImplicitlyOverriddenBaseClassMethod(methodDefinition) as IMethodDefinition; while (overriddenMethod != null && !(overriddenMethod is Dummy)) { IMethodContract/*?*/ overriddenContract = ContractHelper.GetMethodContractFor(host, overriddenMethod); if (overriddenContract != null) { overriddenContract = CopyContractIntoNewContext(host, overriddenContract, methodDefinition, overriddenMethod); overriddenContract = FilterUserMessageAndLegacyPreconditions(host, overriddenContract, methodDefinition.ContainingTypeDefinition, keepLegacyPreconditions); // if the method is generic, then need to specialize the contract to have the same method type parameters as the method if (methodDefinition.IsGeneric) { var d = new Dictionary<uint, ITypeReference>(); IteratorHelper.Zip(overriddenMethod.GenericParameters, methodDefinition.GenericParameters, (i, j) => d.Add(i.InternedKey, j)); var cs = new CodeSpecializer(host, d); overriddenContract = cs.Rewrite(overriddenContract); } Microsoft.Cci.MutableContracts.ContractHelper.AddMethodContract(cumulativeContract, overriddenContract); atLeastOneContract = true; } overriddenMethod = MemberHelper.GetImplicitlyOverriddenBaseClassMethod(overriddenMethod) as IMethodDefinition; } } #endregion Overrides of base class methods #region Implicit interface implementations foreach (IMethodDefinition ifaceMethod in ContractHelper.GetAllImplicitlyImplementedInterfaceMethods(methodDefinition)) { IMethodContract/*?*/ ifaceContract = ContractHelper.GetMethodContractFor(host, ifaceMethod); if (ifaceContract == null) continue; ifaceContract = CopyContractIntoNewContext(host, ifaceContract, methodDefinition, ifaceMethod); ifaceContract = FilterUserMessageAndLegacyPreconditions(host, ifaceContract, methodDefinition.ContainingTypeDefinition, keepLegacyPreconditions); Microsoft.Cci.MutableContracts.ContractHelper.AddMethodContract(cumulativeContract, ifaceContract); atLeastOneContract = true; } #endregion Implicit interface implementations #region Explicit interface implementations and explicit method overrides foreach (IMethodReference ifaceMethodRef in MemberHelper.GetExplicitlyOverriddenMethods(methodDefinition)) { IMethodDefinition/*?*/ ifaceMethod = ifaceMethodRef.ResolvedMethod; if (ifaceMethod == null) continue; IMethodContract/*?*/ ifaceContract = ContractHelper.GetMethodContractFor(host, ifaceMethod); if (ifaceContract == null) continue; ifaceContract = CopyContractIntoNewContext(host, ifaceContract, methodDefinition, ifaceMethod); ifaceContract = FilterUserMessageAndLegacyPreconditions(host, ifaceContract, methodDefinition.ContainingTypeDefinition, keepLegacyPreconditions); Microsoft.Cci.MutableContracts.ContractHelper.AddMethodContract(cumulativeContract, ifaceContract); atLeastOneContract = true; } #endregion Explicit interface implementations and explicit method overrides return atLeastOneContract ? cumulativeContract : null; }
/// <summary> /// Given a method definition for a getter or setter that the compiler produced for an auto-property, /// mine the type contract and extract contracts from any invariants that mention the property. /// If the <paramref name="methodDefinition"/> is a getter, then the returned method contract contains /// only postconditions. /// If the <paramref name="methodDefinition"/> is a setter, then the returned method contract contains /// only preconditions. /// If an invariant does not mention the property, then it is not represented in the returned contract. /// </summary> /// <param name="host"></param> /// <param name="typeContract"> /// This must be the type contract corresponding to the containing type of <paramref name="methodDefinition"/>. /// </param> /// <param name="methodDefinition"> /// A method definition that should be a getter or setter for an auto-property. If it is not, then null is returned. /// </param> /// <returns>Either null or a method contract containing pre- or postconditions (mutually exclusive) /// mined from the invariants contained in the <paramref name="typeContract"/>. /// </returns> public static MethodContract/*?*/ GetAutoPropertyContract(IMetadataHost host, ITypeContract typeContract, IMethodDefinition methodDefinition) { // If the method was generated for an auto-property, then need to see if a contract can be derived by mining the invariant. if (!methodDefinition.IsSpecialName) return null; bool isPropertyGetter = methodDefinition.Name.Value.StartsWith("get_"); bool isPropertySetter = methodDefinition.Name.Value.StartsWith("set_"); //^ assume !(isPropertyGetter && isPropertySetter); // maybe neither, but never both! if (!ContractHelper.IsAutoPropertyMember(host, methodDefinition)) return null; IMethodDefinition getter = null; IMethodDefinition setter = null; // needs to have both a setter and a getter var ct = methodDefinition.ContainingTypeDefinition; if (isPropertyGetter) { getter = methodDefinition; var mms = ct.GetMatchingMembersNamed(host.NameTable.GetNameFor("set_" + methodDefinition.Name.Value.Substring(4)), false, md => ContractHelper.IsAutoPropertyMember(host, md)); foreach (var mem in mms) { setter = mem as IMethodDefinition; break; } } else { // isPropertySetter setter = methodDefinition; var mms = ct.GetMatchingMembersNamed(host.NameTable.GetNameFor("get_" + methodDefinition.Name.Value.Substring(4)), false, md => ContractHelper.IsAutoPropertyMember(host, md)); foreach (var mem in mms) { getter = mem as IMethodDefinition; break; } } // Silent error? if (getter == null || setter == null) return null; // If the auto-property inherits any contracts then it doesn't derive any from the invariant var inheritsContract = false; IMethodDefinition overriddenMethod = MemberHelper.GetImplicitlyOverriddenBaseClassMethod(getter) as IMethodDefinition; var isOverride = getter.IsNewSlot && overriddenMethod != null && !(overriddenMethod is Dummy); inheritsContract |= isOverride; if (!inheritsContract) { inheritsContract |= IteratorHelper.EnumerableIsNotEmpty(ContractHelper.GetAllImplicitlyImplementedInterfaceMethods(getter)); } if (!inheritsContract) { inheritsContract |= IteratorHelper.EnumerableIsNotEmpty(MemberHelper.GetExplicitlyOverriddenMethods(getter)); } if (inheritsContract) return null; if (typeContract == null) return null; MethodContract derivedMethodContract = null; if (isPropertyGetter) { var derivedPostConditions = new List<IPostcondition>(); foreach (var i in typeContract.Invariants) { if (!MemberFinder.ExpressionContains(i.Condition, getter)) continue; var v = Visibility.MostRestrictiveVisibility(host, i.Condition); var currentVisibility = getter.Visibility; var intersection = TypeHelper.VisibilityIntersection(v, currentVisibility); if (intersection != currentVisibility) continue; derivedPostConditions.Add( new Postcondition() { Condition = ReplaceAutoPropGetter.MakeEnsures(host, getter, i.Condition), Description = i.Description, OriginalSource = i.OriginalSource, Locations = new List<ILocation>(i.Locations), }); } if (0 < derivedPostConditions.Count) { derivedMethodContract = new MethodContract() { Postconditions = derivedPostConditions, }; } } else { // isPropertySetter var derivedPreconditions = new List<IPrecondition>(); foreach (var i in typeContract.Invariants) { if (!MemberFinder.ExpressionContains(i.Condition, getter)) continue; var v = Visibility.MostRestrictiveVisibility(host, i.Condition); var currentVisibility = setter.Visibility; var intersection = TypeHelper.VisibilityIntersection(v, currentVisibility); if (intersection != currentVisibility) continue; derivedPreconditions.Add( new Precondition() { Condition = ReplaceAutoPropGetter.MakeRequires(host, getter, setter, i.Condition), Description = i.Description, OriginalSource = i.OriginalSource, Locations = new List<ILocation>(i.Locations), }); } if (0 < derivedPreconditions.Count) { derivedMethodContract = new MethodContract() { Preconditions = derivedPreconditions, }; } } return derivedMethodContract; }