/// <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) { var cachedContract = this.contractProviderCache.GetMethodContractFor(method); if (cachedContract != null) { return(cachedContract == ContractDummy.MethodContract ? null : cachedContract); } IMethodReference methodReference = method as IMethodReference; if (methodReference == null) { this.contractProviderCache.AssociateMethodWithContract(method, ContractDummy.MethodContract); return(null); } IMethodDefinition methodDefinition = methodReference.ResolvedMethod; if (methodDefinition is Dummy) { this.contractProviderCache.AssociateMethodWithContract(method, ContractDummy.MethodContract); return(null); } var underlyingContract = this.underlyingContractProvider.GetMethodContractFor(method); if (!methodDefinition.IsAbstract) { if (underlyingContract != null) { return(underlyingContract); } else { this.contractProviderCache.AssociateMethodWithContract(method, ContractDummy.MethodContract); return(null); } } // The method is definitely an abstract method, so either: // (a) we've never looked for a contract for it before, or else // (b) it is a specialized/instantiated method and the uninstantiated version has already // had its contract extracted. var unspecializedMethodDefinition = ContractHelper.UninstantiateAndUnspecializeMethodDefinition(methodDefinition); cachedContract = this.contractProviderCache.GetMethodContractFor(unspecializedMethodDefinition); if (cachedContract == null) // (a) // Check to see if its containing type points to a class holding the contract { IMethodDefinition /*?*/ proxyMethod = ContractHelper.GetMethodFromContractClass(this.host, unspecializedMethodDefinition); if (proxyMethod == null) { this.contractProviderCache.AssociateMethodWithContract(method, ContractDummy.MethodContract); return(null); } MethodContract cumulativeContract = new MethodContract(); if (underlyingContract != null) { ContractHelper.AddMethodContract(cumulativeContract, underlyingContract); } IMethodContract proxyContract = this.underlyingContractProvider.GetMethodContractFor(proxyMethod); ITypeReference contractClass = proxyMethod.ContainingTypeDefinition; var gtir = contractClass as IGenericTypeInstanceReference; if (gtir != null) { contractClass = gtir.GenericType; } if (proxyContract == null) { if (underlyingContract == null) { // then there was nothing on the abstract method (like purity markings) this.contractProviderCache.AssociateMethodWithContract(method, ContractDummy.MethodContract); return(null); } else { // nothing on proxy, but something on abstract method this.contractProviderCache.AssociateMethodWithContract(method, cumulativeContract); return(cumulativeContract); } } var copier = new CodeAndContractDeepCopier(this.host); proxyContract = copier.Copy(proxyContract); var cccc = new ConvertContractClassContract(this.host, contractClass, unspecializedMethodDefinition.ContainingType); proxyContract = cccc.Rewrite(proxyContract); proxyContract = ContractHelper.CopyContractIntoNewContext(this.host, proxyContract, unspecializedMethodDefinition, proxyMethod); ContractHelper.AddMethodContract(cumulativeContract, proxyContract); // Cache the unspecialized contract: specialize and instantiate on demand this.contractProviderCache.AssociateMethodWithContract(unspecializedMethodDefinition, cumulativeContract); cachedContract = cumulativeContract; } if (unspecializedMethodDefinition == methodDefinition) { return(cachedContract == ContractDummy.MethodContract ? null : cachedContract); } else // (b) { var mc = ContractHelper.InstantiateAndSpecializeContract(this.host, cachedContract, methodDefinition, unspecializedMethodDefinition); mc = (MethodContract)ContractHelper.CopyContractIntoNewContext(this.host, mc, methodDefinition, unspecializedMethodDefinition); return(mc); } }
/// <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) { IMethodContract contract = this.underlyingContractProvider.GetMethodContractFor(method); if (contract != null) { return(contract == ContractDummy.MethodContract ? null : contract); } IMethodReference methodReference = method as IMethodReference; if (methodReference == null) { this.underlyingContractProvider.AssociateMethodWithContract(method, ContractDummy.MethodContract); return(null); } IMethodDefinition methodDefinition = methodReference.ResolvedMethod; if (methodDefinition is Dummy) { this.underlyingContractProvider.AssociateMethodWithContract(method, ContractDummy.MethodContract); return(null); } if (methodDefinition.IsAbstract || methodDefinition.IsExternal) // precondition of Body getter // Need to see if the method is marked with any attributes that impact the contract { if (ContractHelper.IsPure(this.host, methodDefinition)) { var pureMC = new MethodContract() { IsPure = true, }; this.underlyingContractProvider.AssociateMethodWithContract(method, pureMC); return(pureMC); } else { this.underlyingContractProvider.AssociateMethodWithContract(method, ContractDummy.MethodContract); return(null); } } var unspecializedMethodDefintion = ContractHelper.UninstantiateAndUnspecializeMethodDefinition(methodDefinition); if (unspecializedMethodDefintion != methodDefinition) { contract = this.underlyingContractProvider.GetMethodContractFor(unspecializedMethodDefintion); if (contract != null) { return(ContractHelper.InstantiateAndSpecializeContract(this.host, contract, methodDefinition, unspecializedMethodDefintion)); } } IMethodBody methodBody = unspecializedMethodDefintion.Body; if (methodBody is Dummy) { this.underlyingContractProvider.AssociateMethodWithContract(method, ContractDummy.MethodContract); return(null); } ISourceMethodBody /*?*/ sourceMethodBody = methodBody as ISourceMethodBody; if (sourceMethodBody == null) { sourceMethodBody = Decompiler.GetCodeModelFromMetadataModel(this.host, methodBody, this.pdbReader, DecompilerOptions.AnonymousDelegates); } MethodContractAndMethodBody result = this.SplitMethodBodyIntoContractAndCode(sourceMethodBody); var methodContract = result.MethodContract; if (methodContract != null && unspecializedMethodDefintion != methodDefinition) { var instantiatedContract = ContractHelper.InstantiateAndSpecializeContract(this.host, result.MethodContract, methodDefinition, unspecializedMethodDefintion); methodContract = instantiatedContract; } #region Auto-properties get their contract from mining the invariant if (ContractHelper.IsAutoPropertyMember(host, unspecializedMethodDefintion)) { var tc = this.GetTypeContractFor(unspecializedMethodDefintion.ContainingTypeDefinition); MethodContract mc = ContractHelper.GetAutoPropertyContract(this.host, tc, unspecializedMethodDefintion); if (mc != null) { if (unspecializedMethodDefintion != methodDefinition) { var mutableContract = ContractHelper.InstantiateAndSpecializeContract(this.host, mc, methodDefinition, unspecializedMethodDefintion); mc = mutableContract; } if (methodContract == null) { methodContract = mc; } else { ContractHelper.AddMethodContract(mc, methodContract); } } } #endregion if (methodContract == null) { this.underlyingContractProvider.AssociateMethodWithContract(method, ContractDummy.MethodContract); // so we don't try to extract more than once } else { this.underlyingContractProvider.AssociateMethodWithContract(method, methodContract); } // Notify all interested parties foreach (var c in this.callbacks) { c.ProvideResidualMethodBody(methodDefinition, result.BlockStatement); } return(methodContract); }