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