/// <summary> /// Given a mutable module that is a "declarative" module, i.e., it has contracts expressed as contract calls /// at the beginning of method bodies, this method will extract them, leaving the method bodies without those /// calls and return a contract provider for the module containing any extracted contracts. /// </summary> public static ContractProvider ExtractContracts(IContractAwareHost host, Module module, PdbReader/*?*/ pdbReader, ILocalScopeProvider/*?*/ localScopeProvider) { var contractMethods = new ContractMethods(host); var cp = new Microsoft.Cci.MutableContracts.ContractProvider(contractMethods, module); var extractor = new SeparateContractsFromCode(host, pdbReader, localScopeProvider, cp); extractor.Traverse(module); return cp; }
/// <summary> /// If the unit is a reference assembly, then just attach a contract extractor to it. /// Otherwise, create an aggregating extractor that encapsulates the unit and any /// reference assemblies that are found on the search path. /// Each contract extractor is actually a composite comprising a code-contracts /// extractor layered on top of a lazy extractor. /// </summary> protected void AttachContractExtractorAndLoadReferenceAssembliesFor(IUnit alreadyLoadedUnit) { // Because of unification, the "alreadyLoadedUnit" might have actually already been loaded previously // and gone through here (and so already has a contract provider attached to it). if (this.unit2ContractExtractor.ContainsKey(alreadyLoadedUnit.UnitIdentity)) this.unit2ContractExtractor.Remove(alreadyLoadedUnit.UnitIdentity); //return; var contractMethods = new ContractMethods(this); using (var lazyContractProviderForLoadedUnit = new LazyContractExtractor(this, alreadyLoadedUnit, contractMethods, this.AllowExtractorsToUsePdbs)) { var contractProviderForLoadedUnit = new CodeContractsContractExtractor(this, lazyContractProviderForLoadedUnit); if (ContractHelper.IsContractReferenceAssembly(this, alreadyLoadedUnit)) { // If we're asked to explicitly load a reference assembly, then go ahead and attach a contract provider to it, // but *don't* look for reference assemblies for *it*. this.unit2ContractExtractor.Add(alreadyLoadedUnit.UnitIdentity, contractProviderForLoadedUnit); } else { #region Load any reference assemblies for the loaded unit var loadedAssembly = alreadyLoadedUnit as IAssembly; // Only assemblies can have associated reference assemblies. var oobProvidersAndHosts = new List<KeyValuePair<IContractProvider, IMetadataHost>>(); if (loadedAssembly != null) { var refAssemWithoutLocation = new AssemblyIdentity(this.NameTable.GetNameFor(alreadyLoadedUnit.Name.Value + ".Contracts"), loadedAssembly.AssemblyIdentity.Culture, loadedAssembly.AssemblyIdentity.Version, loadedAssembly.AssemblyIdentity.PublicKeyToken, ""); var referenceAssemblyIdentity = this.ProbeAssemblyReference(alreadyLoadedUnit, refAssemWithoutLocation); IUnit referenceUnit = null; IContractAwareHost hostForReferenceAssembly = this; // default if (referenceAssemblyIdentity.Location.Equals("unknown://location")) { // It might be the case that this was returned because the identity constructed for it had the wrong version number // (or something else that didn't match the identity of hte already loaded unit). But it might be that the probing // logic succeeded in loading *some* reference assembly. And we're not picky: the reference assembly is just // the first assembly found with the right name. foreach (var u in this.LoadedUnits) { if (u.Name.Equals(refAssemWithoutLocation.Name)) { // fine, use this one! referenceUnit = u; break; } } if (referenceUnit != null && loadedAssembly.AssemblyIdentity.Equals(this.CoreAssemblySymbolicIdentity)) { // Need to use a separate host because the reference assembly for the core assembly thinks *it* is the core assembly var separateHost = new SimpleHostEnvironment(this.NameTable, this.InternFactory, this.PreserveILLocations); this.disposableObjectAllocatedByThisHost.Add(separateHost); referenceUnit = separateHost.LoadUnitFrom(referenceUnit.Location); hostForReferenceAssembly = separateHost; } } else { // referenceAssemblyIdentity.Location != "unknown://location") #region Load reference assembly if (loadedAssembly.AssemblyIdentity.Equals(this.CoreAssemblySymbolicIdentity)) { // Need to use a separate host because the reference assembly for the core assembly thinks *it* is the core assembly var separateHost = new SimpleHostEnvironment(this.NameTable, this.InternFactory, this.PreserveILLocations); this.disposableObjectAllocatedByThisHost.Add(separateHost); referenceUnit = separateHost.LoadUnitFrom(referenceAssemblyIdentity.Location); hostForReferenceAssembly = separateHost; } else { // Load reference assembly, but don't cause a recursive call!! So don't call LoadUnit or LoadUnitFrom referenceUnit = this.peReader.OpenModule(BinaryDocument.GetBinaryDocumentForFile(referenceAssemblyIdentity.Location, this)); this.RegisterAsLatest(referenceUnit); } #endregion } #region Attach a contract provider to it if (referenceUnit != null && !(referenceUnit is Dummy)) { IAssembly referenceAssembly = referenceUnit as IAssembly; if (referenceAssembly != null) { var referenceAssemblyContractProvider = new CodeContractsContractExtractor(hostForReferenceAssembly, new LazyContractExtractor(hostForReferenceAssembly, referenceAssembly, contractMethods, this.AllowExtractorsToUsePdbs)); oobProvidersAndHosts.Add(new KeyValuePair<IContractProvider, IMetadataHost>(referenceAssemblyContractProvider, hostForReferenceAssembly)); if (!this.unit2ReferenceAssemblies.ContainsKey(alreadyLoadedUnit)) { this.unit2ReferenceAssemblies[alreadyLoadedUnit] = new List<IUnitReference>(); } // Reference assemblies don't have references to the real assembly but they are "dependent" // on them in that they should be dumped and reloaded if the real assembly changes. this.unit2ReferenceAssemblies[alreadyLoadedUnit].Add(referenceAssembly); } } #endregion } var aggregateContractProvider = new AggregatingContractExtractor(this, contractProviderForLoadedUnit, oobProvidersAndHosts); this.unit2ContractExtractor.Add(alreadyLoadedUnit.UnitIdentity, aggregateContractProvider); #endregion Load any reference assemblies for the loaded unit } foreach (var c in this.callbacks) { contractProviderForLoadedUnit.RegisterContractProviderCallback(c); } } }