// Requires: // replacementMethod.Keys == replacementExceptions.Keys // public RewriteInvariant(RuntimeContractMethods rcm) { this.rcm = rcm; }
private static void MikesArchitecture(AssemblyResolver resolver, AssemblyNode assemblyNode, ContractNodes contractNodes, ContractNodes backupContracts) { #if false var originalsourceDir = Path.GetDirectoryName(assemblyNode.Location); int oldPeVerifyCode = options.verify ? PEVerify(assemblyNode.Location, originalsourceDir) : -1; #endif // Check to see if the assembly has already been rewritten if (!options.passthrough) { if (ContractNodes.IsAlreadyRewritten(assemblyNode)) { if (!options.allowRewritten) { Console.WriteLine("Assembly '" + assemblyNode.Name + "' has already been rewritten. I, your poor humble servant, cannot rewrite it. Instead I must give up without rewriting it. Help!"); } return; } } // Extract the contracts from the code (includes checking the contracts) string contractFileName = Path.GetFileNameWithoutExtension(assemblyNode.Location) + ".Contracts"; if (options.contracts == null || options.contracts.Count <= 0) contractFileName = null; if (options.contracts != null && !options.contracts.Exists(name => name.Equals(assemblyNode.Name + ".Contracts.dll", StringComparison.OrdinalIgnoreCase))) { contractFileName = null; } AssemblyNode contractAssembly = null; if (contractFileName != null) { contractAssembly = resolver.ProbeForAssembly(contractFileName, assemblyNode.Directory, resolver.DllExt); } if (!options.passthrough) { ContractNodes usedContractNodes; Extractor.ExtractContracts(assemblyNode, contractAssembly, contractNodes, backupContracts, contractNodes, out usedContractNodes, options.EmitError, false); // important to extract source before we perform any more traversals due to contract instantiation. Otherwise, // we might get copies of contracts due to instantiation that have no source text yet. // Extract the text from the sources (optional) if (usedContractNodes != null && options.extractSourceText) { GenerateDocumentationFromPDB gd = new GenerateDocumentationFromPDB(contractNodes); gd.VisitForDoc(assemblyNode); } // After all contracts have been extracted in assembly, do some post-extractor checks // we run these even if no contracts were extracted due to checks having to do with overrides var contractNodesForChecks = usedContractNodes != null ? usedContractNodes : contractNodes; if (contractNodesForChecks != null) { PostExtractorChecker pec = new PostExtractorChecker(contractNodesForChecks, options.EmitError, false, options.fSharp, options.IsLegacyModeAssembly, options.addInterfaceWrappersWhenNeeded, options.level); if (contractAssembly != null) { pec.VisitForPostCheck(contractAssembly); } else { pec.VisitForPostCheck(assemblyNode); } } // don't really need to test, since if they are the same, the assignment doesn't change that // but this is to emphasize that the nodes used in the AST by the extractor are different // than what we thought we were using. if (options.GetErrorCount() > 0) { // we are done. // But first, report any metadata errors so they are not masked by the errors CheckForMetaDataErrors(assemblyNode); return; } } // If we have metadata errors, cop out { #if false for (int i = 0; i < assemblyNode.ModuleReferences.Count; i++) { Module m = assemblyNode.ModuleReferences[i].Module; Console.WriteLine("Location for referenced module '{0}' is '{1}'", m.Name, m.Location); } #endif if (CheckForMetaDataErrors(assemblyNode)) { throw new Exception("Rewrite aborted due to metadata errors. Check output window"); } for (int i = 0; i < assemblyNode.AssemblyReferences.Count; i++) { AssemblyNode aref = assemblyNode.AssemblyReferences[i].Assembly; if (CheckForMetaDataErrors(aref)) { throw new Exception("Rewrite aborted due to metadata errors. Check output window"); } } } // Inject the contracts into the code (optional) if (options.rewrite && !options.passthrough) { // Rewrite the assembly in memory. ContractNodes cnForRuntime = null; // make sure to use the correct contract nodes for runtime code generation. We may have Contractnodes pointing to microsoft.Contracts even though // the code relies on mscorlib to provide the contracts. So make sure the code references the contract nodes first. if (contractNodes != null && contractNodes.ContractClass != null && contractNodes.ContractClass.DeclaringModule != SystemTypes.SystemAssembly) { string assemblyNameContainingContracts = contractNodes.ContractClass.DeclaringModule.Name; for (int i = 0; i < assemblyNode.AssemblyReferences.Count; i++) { if (assemblyNode.AssemblyReferences[i].Name == assemblyNameContainingContracts) { cnForRuntime = contractNodes; break; // runtime actually references the contract library } } } if (cnForRuntime == null) { // try to grab the system assembly contracts cnForRuntime = ContractNodes.GetContractNodes(SystemTypes.SystemAssembly, null); } if (cnForRuntime == null) { // Can happen if the assembly does not use contracts directly, but inherits them from some other place // Use the normal contractNodes in this case (actually we should generate whatever we grab from ContractNodes) cnForRuntime = contractNodes; } RuntimeContractMethods runtimeContracts = new RuntimeContractMethods(userSpecifiedContractType, cnForRuntime, assemblyNode, options.throwOnFailure, options.level, options.publicSurfaceOnly, options.callSiteRequires, options.recursionGuard, options.hideFromDebugger, options.IsLegacyModeAssembly); Rewriter rewriter = new Rewriter(assemblyNode, runtimeContracts, options.EmitError, options.inheritInvariants, options.skipQuantifiers); rewriter.Verbose = 0 < options.verbose; rewriter.Visit(assemblyNode); // Perform this check only when there are no out-of-band contracts in use due to rewriter bug #336 if (contractAssembly == null) { PostRewriteChecker checker = new PostRewriteChecker(options.EmitError); checker.Visit(assemblyNode); } } //Console.WriteLine(">>>Finished Rewriting<<<"); // Set metadata version for target the same as for the source TargetPlatform.TargetRuntimeVersion = assemblyNode.TargetRuntimeVersion; // Write out the assembly (optional) if (options.rewrite || options.passthrough) { bool updateInPlace = options.output == "same"; string pdbFile = Path.ChangeExtension(options.assembly, ".pdb"); bool pdbExists = File.Exists(pdbFile); string backupAssembly = options.assembly + ".original"; string backupPDB = pdbFile + ".original"; if (updateInPlace) { // Write the rewritten assembly in a temporary location. options.output = options.assembly; MoveAssemblyFileAndPDB(options.output, pdbFile, pdbExists, backupAssembly, backupPDB); } // Write the assembly. // Don't pass the debugInfo flag to WriteModule unless the PDB file exists. assemblyNode.WriteModule(options.output, options.debug && pdbExists && options.writePDBFile); string outputDir = updateInPlace ? Path.GetDirectoryName(options.assembly) : Path.GetDirectoryName(options.output); // Re-attach external file resources to the new output assembly. MoveModuleResources(assemblyNode, outputDir); #if false if (oldPeVerifyCode == 0) { var newPeVerifyCode = PEVerify(assemblyNode.Location, originalsourceDir); if (newPeVerifyCode > 0) { if (updateInPlace) { // move original back in place MoveAssemblyFileAndPDB(backupAssembly, backupPDB, pdbExists, options.output, pdbFile); } throw new Exception("Rewrite failed to produce verifiable assembly"); } else if (newPeVerifyCode == 0) { Console.WriteLine("rewriter output verified"); } } #endif if (updateInPlace) { if (!options.keepOriginalFiles) { try { File.Delete(backupAssembly); } catch { // there are situations where the exe is still in use } if (options.debug && pdbExists && options.writePDBFile) { try { File.Delete(backupPDB); } catch { // I know this is stupid, but somehow on some machines we get an AccessError trying to delete the pdb. // so we leave it in place. } } } } } }