/// <summary>
        /// Generate the comparability information for the given assembly.
        /// </summary>
        private static AssemblySummary GenerateComparability(CeleriacArgs celeriacArgs, TypeManager typeManager,
                                                             IMetadataHost host, IModule module, PdbReader pdbReader, ref Assembly mutable)
        {
            AssemblySummary comparabilityManager = null;
            IAssembly       assembly             = module as IAssembly;

            mutable = MetadataCopier.DeepCopy(host, assembly);
            typeManager.SetAssemblyIdentity(UnitHelper.GetAssemblyIdentity(mutable));

            if (celeriacArgs.StaticComparability || celeriacArgs.GenerateComparability)
            {
                if (celeriacArgs.ComparabilityFile != null)
                {
                    using (var cmp = File.Open(celeriacArgs.ComparabilityFile, FileMode.Open))
                    {
                        comparabilityManager = (AssemblySummary) new BinaryFormatter().Deserialize(cmp);
                    }
                }
                else
                {
                    if (celeriacArgs.VerboseMode)
                    {
                        Console.WriteLine("Generating Comparability Information");
                    }

                    Assembly decompiled = Decompiler.GetCodeModelFromMetadataModel(typeManager.Host, mutable,
                                                                                   pdbReader, DecompilerOptions.AnonymousDelegates | DecompilerOptions.Iterators);
                    comparabilityManager = AssemblySummary.MakeSummary(decompiled, typeManager);

                    if (celeriacArgs.VerboseMode)
                    {
                        Console.WriteLine("Finished Generating Comparability Information");
                    }

                    using (var cmp = File.Open(celeriacArgs.AssemblyPath + CeleriacArgs.ComparabilityFileExtension, FileMode.Create))
                    {
                        new BinaryFormatter().Serialize(cmp, comparabilityManager);
                    }
                }
            }
            return(comparabilityManager);
        }
        /// <summary>
        /// Load the PDB reader and pdb file for the source program.
        /// </summary>
        private static PdbReader LoadPdbReaderAndFile(CeleriacArgs celeriacArgs, TypeManager typeManager, IModule module, string pdbFile)
        {
#if __MonoCS__
            if (celeriacArgs.StaticComparability)
            {
                throw new NotSupportedException("Static comparability analysis is not supported on Mono");
            }
            else
            {
                Console.Error.WriteLine("WARNING: Program database (PDB) information is not supported on Mono");
                Console.Error.WriteLine("Celeriac will attempt to continue, but might fail.");
                return(null);
            }
#else
            PdbReader pdbReader = null;
            try
            {
                using (var pdbStream = File.OpenRead(pdbFile))
                {
                    pdbReader = new PdbReader(pdbStream, typeManager.Host);
                }
                return(pdbReader);
            }
            catch (System.IO.IOException ex)
            {
                if (celeriacArgs.StaticComparability)
                {
                    throw new InvalidOperationException("Error loading program database (PDB) file for '" + module.Name.Value + "'. Debug information is required for static comparability analysis.", ex);
                }
                else
                {
                    // It seems to be non-fatal, so print the error and continue.
                    Console.Error.WriteLine("WARNING: Could not load the PDB file for '" + module.Name.Value + "'");
                    Console.Error.WriteLine("Celeriac will attempt to continue, but might fail.");
                }
                return(null);
            }
#endif
        }
        public static MemoryStream RewriteProgramIL(CeleriacArgs celeriacArgs, TypeManager typeManager)
        {
            if (String.IsNullOrWhiteSpace(celeriacArgs.AssemblyPath))
            {
                throw new FileNotFoundException("Path to program to be profiled not provided");
            }

            Stream resultStream;
            var    host = typeManager.Host;

            IModule /*?*/ module = host.LoadUnitFrom(celeriacArgs.AssemblyPath) as IModule;

            if (module == null || module == Dummy.Module || module == Dummy.Assembly)
            {
                throw new FileNotFoundException("Given path is not a PE file containing a CLR"
                                                + " assembly, or an error occurred when loading it.", celeriacArgs.AssemblyPath);
            }

            if (module.GetAllTypes().Any(
                    type => type.Name.ToString().Equals(ILRewriter.ArgumentStoringClassName)))
            {
                throw new InvalidOperationException("Program has already been instrumented.");
            }

            string   pdbFile = Path.ChangeExtension(module.Location, "pdb");
            Assembly mutable = null;

            using (var pdbReader = LoadPdbReaderAndFile(celeriacArgs, typeManager, module, pdbFile))
            {
                AssemblySummary comparabilityManager = GenerateComparability(celeriacArgs, typeManager,
                                                                             host, module, pdbReader, ref mutable);

                if (celeriacArgs.GenerateComparability && celeriacArgs.ComparabilityFile == null)
                {
                    return(null);
                }

                ILRewriter mutator = new ILRewriter(host, pdbReader, celeriacArgs, typeManager, comparabilityManager);

                module = mutator.Visit(mutable, Path.Combine(FindVisitorDir(), VisitorDll));

                if (celeriacArgs.EmitNullaryInfo || celeriacArgs.GenerateComparability)
                {
                    return(null);
                }

                // Remove the old PDB file
                try
                {
                    File.Delete(pdbFile);
                }
                catch (UnauthorizedAccessException)
                {
                    // If they are running the debugger we might not be able to delete the file
                    // Save the pdb elsewhere in this case.
                    pdbFile = module.Location + ".pdb";
                }

                if (celeriacArgs.SaveProgram != null)
                {
                    resultStream = new FileStream(celeriacArgs.SaveProgram, FileMode.Create);
                }
                else
                {
                    resultStream = new MemoryStream();
                }


#if __MonoCS__
                // Reading / Writing DEBUG information on Mono is not supported by CCI
                PdbWriter pdbWriter = null;
#else
                var pdbWriter = new PdbWriter(pdbFile, pdbReader);
#endif

                // Need to not pass in a local scope provider until such time as we have one
                // that will use the mutator to remap things (like the type of a scope
                // constant) from the original assembly to the mutated one.
                using (pdbWriter)
                {
                    PeWriter.WritePeToStream(module, host, resultStream, pdbReader, null, pdbWriter);
                }
            }

            if (celeriacArgs.SaveProgram != null)
            {
                // We aren't going to run the program, so no need to return anything,
                // but close the file stream.
                resultStream.Close();
                return(null);
            }
            else
            {
                return((MemoryStream)resultStream); // success
            }
        }