예제 #1
0
 /// <summary>
 /// Main entry point to the CIL extractor.
 /// Call this to extract a given assembly.
 /// </summary>
 /// <param name="layout">The trap layout.</param>
 /// <param name="assemblyPath">The full path of the assembly to extract.</param>
 /// <param name="logger">The logger.</param>
 /// <param name="nocache">True to overwrite existing trap file.</param>
 /// <param name="extractPdbs">Whether to extract PDBs.</param>
 /// <param name="trapFile">The path of the trap file.</param>
 /// <param name="extracted">Whether the file was extracted (false=cached).</param>
 public static void ExtractCIL(Layout layout, string assemblyPath, ILogger logger, bool nocache, bool extractPdbs, TrapWriter.CompressionMode trapCompression, out string trapFile, out bool extracted)
 {
     trapFile  = "";
     extracted = false;
     try
     {
         var canonicalPathCache      = CanonicalPathCache.Create(logger, 1000);
         var pathTransformer         = new PathTransformer(canonicalPathCache);
         var extractor               = new Extractor(false, assemblyPath, logger, pathTransformer);
         var transformedAssemblyPath = pathTransformer.Transform(assemblyPath);
         var project = layout.LookupProjectOrDefault(transformedAssemblyPath);
         using var trapWriter = project.CreateTrapWriter(logger, transformedAssemblyPath.WithSuffix(".cil"), trapCompression, discardDuplicates: true);
         trapFile             = trapWriter.TrapFile;
         if (nocache || !System.IO.File.Exists(trapFile))
         {
             var cx = extractor.CreateContext(null, trapWriter, null, false);
             ExtractCIL(cx, assemblyPath, extractPdbs);
             extracted = true;
         }
     }
     catch (Exception ex)  // lgtm[cs/catch-of-all-exceptions]
     {
         logger.Log(Severity.Error, string.Format("Exception extracting {0}: {1}", assemblyPath, ex));
     }
 }
예제 #2
0
        public void TestTransformerFile()
        {
            var spec = new string[]
            {
                @"#D:\src",
                @"C:\agent*\src//",
                @"-C:\agent*\src\external",
                @"",
                @"#empty",
                @"",
                @"#src2",
                @"/agent*//src",
                @"",
                @"#optsrc",
                @"opt/src//"
            };

            var pathTransformer = new PathTransformer(new PathCacheStub(), spec);

            // Windows-style matching
            Assert.Equal(@"C:/bar.cs", pathTransformer.Transform(@"C:\bar.cs").Value);
            Assert.Equal("D:/src/file.cs", pathTransformer.Transform(@"C:\agent42\src\file.cs").Value);
            Assert.Equal("D:/src/file.cs", pathTransformer.Transform(@"C:\agent43\src\file.cs").Value);
            Assert.Equal(@"C:/agent43/src/external/file.cs", pathTransformer.Transform(@"C:\agent43\src\external\file.cs").Value);

            // Linux-style matching
            Assert.Equal(@"src2/src/file.cs", pathTransformer.Transform(@"/agent/src/file.cs").Value);
            Assert.Equal(@"src2/src/file.cs", pathTransformer.Transform(@"/agent42/src/file.cs").Value);
            Assert.Equal(@"optsrc/file.cs", pathTransformer.Transform(@"/opt/src/file.cs").Value);
        }
예제 #3
0
        public void LeftSidePath_FromRightAbsolutPath()
        {
            PathTransformer pathTransformer = new PathTransformer("http://somedomain/", "http://egoratest/somedomain/");
            string          newPath         = pathTransformer.AdjustPath("/page1.html");

            Assert.IsNotNull(newPath);
            Assert.AreEqual("http://egoratest/somedomain/page1.html", newPath);
        }
예제 #4
0
        public void LocationHeaderTest()
        {
            string location =
                "http://localhost:9090/fkb2local/views/client.api.DatenImportierenLandesIT.jsf?FT=X";

            PathTransformer transformer = new PathTransformer("http://localhost:9090/fkb2local/", "/bmi.gv.at/fk2web-t/");

            Assert.AreEqual("/bmi.gv.at/fk2web-t/views/client.api.DatenImportierenLandesIT.jsf?FT=X", transformer.AdjustPath(location));
        }
예제 #5
0
        /// <summary>
        ///     Extract an assembly to a new trap file.
        ///     If the trap file exists, skip extraction to avoid duplicating
        ///     extraction within the snapshot.
        /// </summary>
        /// <param name="r">The assembly to extract.</param>
        private void DoAnalyseReferenceAssembly(PortableExecutableReference r)
        {
            try
            {
                var stopwatch = new Stopwatch();
                stopwatch.Start();

                var assemblyPath            = r.FilePath;
                var transformedAssemblyPath = PathTransformer.Transform(assemblyPath);
                var projectLayout           = layout.LookupProjectOrDefault(transformedAssemblyPath);
                using var trapWriter = projectLayout.CreateTrapWriter(Logger, transformedAssemblyPath, options.TrapCompression, discardDuplicates: true);

                var skipExtraction = options.Cache && File.Exists(trapWriter.TrapFile);

                if (!skipExtraction)
                {
                    /* Note on parallel builds:
                     *
                     * The trap writer and source archiver both perform atomic moves
                     * of the file to the final destination.
                     *
                     * If the same source file or trap file are generated concurrently
                     * (by different parallel invocations of the extractor), then
                     * last one wins.
                     *
                     * Specifically, if two assemblies are analysed concurrently in a build,
                     * then there is a small amount of duplicated work but the output should
                     * still be correct.
                     */

                    // compilation.Clone() reduces memory footprint by allowing the symbols
                    // in c to be garbage collected.
                    Compilation c = compilation.Clone();


                    if (c.GetAssemblyOrModuleSymbol(r) is IAssemblySymbol assembly)
                    {
                        var cx = new Context(extractor, c, trapWriter, new AssemblyScope(assembly, assemblyPath), AddAssemblyTrapPrefix);

                        foreach (var module in assembly.Modules)
                        {
                            AnalyseNamespace(cx, module.GlobalNamespace);
                        }

                        Entities.Attribute.ExtractAttributes(cx, assembly, Extraction.Entities.Assembly.Create(cx, assembly.GetSymbolLocation()));

                        cx.PopulateAll();
                    }
                }

                ReportProgress(assemblyPath, trapWriter.TrapFile, stopwatch.Elapsed, skipExtraction ? AnalysisAction.UpToDate : AnalysisAction.Extracted);
            }
            catch (Exception ex)  // lgtm[cs/catch-of-all-exceptions]
            {
                Logger.Log(Severity.Error, "  Unhandled exception analyzing {0}: {1}", r.FilePath, ex);
            }
        }
예제 #6
0
        public void LeftSidePath_FromRightAbsolutPathAndNoRootProxyPath()
        {
            PathTransformer pathTransformer = new PathTransformer("https://www.fundamt.gv.at/gondor/",
                                                                  "http://egoratest/stammportal/fundamt/");
            string newPath = pathTransformer.AdjustPath("/gondor/WebGov/UI/Security/FormsLogin.aspx");

            Assert.IsNotNull(newPath);
            Assert.AreEqual("http://egoratest/stammportal/fundamt/WebGov/UI/Security/FormsLogin.aspx", newPath);
        }
예제 #7
0
 public Analyser(IProgressMonitor pm, ILogger logger, bool addAssemblyTrapPrefix, PathTransformer pathTransformer)
 {
     Logger = logger;
     AddAssemblyTrapPrefix = addAssemblyTrapPrefix;
     Logger.Log(Severity.Info, "EXTRACTION STARTING at {0}", DateTime.Now);
     stopWatch.Start();
     progressMonitor = pm;
     PathTransformer = pathTransformer;
 }
예제 #8
0
        public void LocationHeaderTest2()
        {
            string location =
                "http://localhost:9090/fkb2local/views/client.api.DatenImportierenLandesIT.jsf?FT=X";
            string expectedLaoction = location;

            PathTransformer transformer = new PathTransformer("https://pvawp.bmi.gv.at/bmi.gv.at/fk2web-t/", "/bmi.gv.at/fk2web-t/blabla.jsp");

            Assert.AreEqual(expectedLaoction, transformer.AdjustPath(location));
        }
예제 #9
0
        public void LeftSidePath_FromRightAbsolutPathAndApplicationAsSubdir()
        {
            PathTransformer pathTransformer =
                new PathTransformer("https://awp.statistik.at/statistik.at/vis.test.extern/",
                                    "/statistik.at/vis.test.extern/");
            string newPath = pathTransformer.AdjustPath("https://awp.statistik.at/statistik.at/vis.test.extern/start.do");

            Assert.IsNotNull(newPath);
            Assert.AreEqual("/statistik.at/vis.test.extern/start.do", newPath);
        }
예제 #10
0
 public MockRedisContentLocationStoreFactory(
     ITestRedisDatabase redisDatabase,
     ITestRedisDatabase machineLocationRedisDatabase,
     AbsolutePath localCacheRoot,
     IClock mockClock = null,
     RedisContentLocationStoreConfiguration configuration = null)
 {
     LocalMachineData             = PathTransformer.GetLocalMachineLocation(localCacheRoot);
     _mockClock                   = mockClock ?? SystemClock.Instance;
     RedisDatabase                = redisDatabase;
     MachineLocationRedisDatabase = machineLocationRedisDatabase;
     _configuration               = configuration ?? RedisContentLocationStoreConfiguration.Default;
 }
예제 #11
0
        public string TransformsToExpectedResult(string pathToTransform)
        {
            var originalPathsFromManifest = new string[]
            {
                "Spartan.exe",
                "art\\Art.bar",
                "sound\\SoundSets\\Amb.xml"
            };

            var builder     = new PathTransformerBuilder();
            var transformer = new PathTransformer(builder.Build(originalPathsFromManifest));

            return(transformer.TransformPath(pathToTransform));
        }
예제 #12
0
        private void DoExtractTree(SyntaxTree tree)
        {
            try
            {
                var stopwatch = new Stopwatch();
                stopwatch.Start();
                var sourcePath            = tree.FilePath;
                var transformedSourcePath = PathTransformer.Transform(sourcePath);

                var projectLayout = layout.LookupProjectOrNull(transformedSourcePath);
                var excluded      = projectLayout == null;
                var trapPath      = excluded ? "" : projectLayout.GetTrapPath(Logger, transformedSourcePath, options.TrapCompression);
                var upToDate      = false;

                if (!excluded)
                {
                    // compilation.Clone() is used to allow symbols to be garbage collected.
                    using var trapWriter = projectLayout.CreateTrapWriter(Logger, transformedSourcePath, options.TrapCompression, discardDuplicates: false);

                    upToDate = options.Fast && FileIsUpToDate(sourcePath, trapWriter.TrapFile);

                    if (!upToDate)
                    {
                        var cx = new Context(extractor, compilation.Clone(), trapWriter, new SourceScope(tree), AddAssemblyTrapPrefix);
                        // Ensure that the file itself is populated in case the source file is totally empty
                        var root = tree.GetRoot();
                        Extraction.Entities.File.Create(cx, root.SyntaxTree.FilePath);

                        var csNode = (CSharpSyntaxNode)root;
                        csNode.Accept(new CompilationUnitVisitor(cx));
                        csNode.Accept(new DirectiveVisitor(cx));
                        cx.PopulateAll();
                        CommentPopulator.ExtractCommentBlocks(cx, cx.CommentGenerator);
                        cx.PopulateAll();
                    }
                }

                ReportProgress(sourcePath, trapPath, stopwatch.Elapsed, excluded
                    ? AnalysisAction.Excluded
                    : upToDate
                        ? AnalysisAction.UpToDate
                        : AnalysisAction.Extracted);
            }
            catch (Exception ex)  // lgtm[cs/catch-of-all-exceptions]
            {
                extractor.Message(new Message($"Unhandled exception processing syntax tree. {ex.Message}", tree.FilePath, null, ex.StackTrace));
            }
        }
예제 #13
0
        private void DoAnalyseCompilation()
        {
            try
            {
                var assemblyPath            = extractor.OutputPath;
                var transformedAssemblyPath = PathTransformer.Transform(assemblyPath);
                var assembly      = compilation.Assembly;
                var projectLayout = layout.LookupProjectOrDefault(transformedAssemblyPath);
                var trapWriter    = projectLayout.CreateTrapWriter(Logger, transformedAssemblyPath, options.TrapCompression, discardDuplicates: false);
                compilationTrapFile = trapWriter;  // Dispose later
                var cx = new Context(extractor, compilation.Clone(), trapWriter, new AssemblyScope(assembly, assemblyPath), AddAssemblyTrapPrefix);

                compilationEntity = Entities.Compilation.Create(cx);
            }
            catch (Exception ex)  // lgtm[cs/catch-of-all-exceptions]
            {
                Logger.Log(Severity.Error, "  Unhandled exception analyzing {0}: {1}", "compilation", ex);
            }
        }
예제 #14
0
        private void DoExtractTree(SyntaxTree tree)
        {
            try
            {
                var stopwatch = new Stopwatch();
                stopwatch.Start();
                var sourcePath            = tree.FilePath;
                var transformedSourcePath = PathTransformer.Transform(sourcePath);

                var projectLayout = layout.LookupProjectOrNull(transformedSourcePath);
                var excluded      = projectLayout == null;
                var trapPath      = excluded ? "" : projectLayout.GetTrapPath(Logger, transformedSourcePath, options.TrapCompression);
                var upToDate      = false;

                if (!excluded)
                {
                    // compilation.Clone() is used to allow symbols to be garbage collected.
                    using var trapWriter = projectLayout.CreateTrapWriter(Logger, transformedSourcePath, options.TrapCompression, discardDuplicates: false);

                    upToDate = options.Fast && FileIsUpToDate(sourcePath, trapWriter.TrapFile);

                    if (!upToDate)
                    {
                        var cx = extractor.CreateContext(compilation.Clone(), trapWriter, new SourceScope(tree), AddAssemblyTrapPrefix);
                        Populators.CompilationUnit.Extract(cx, tree.GetRoot());
                        cx.PopulateAll();
                        cx.ExtractComments(cx.CommentGenerator);
                        cx.PopulateAll();
                    }
                }

                ReportProgress(sourcePath, trapPath, stopwatch.Elapsed, excluded
                    ? AnalysisAction.Excluded
                    : upToDate
                        ? AnalysisAction.UpToDate
                        : AnalysisAction.Extracted);
            }
            catch (Exception ex)  // lgtm[cs/catch-of-all-exceptions]
            {
                extractor.Message(new Message("Unhandled exception processing syntax tree", tree.FilePath, null, ex.StackTrace));
            }
        }
예제 #15
0
 /// <summary>
 /// Main entry point to the CIL extractor.
 /// Call this to extract a given assembly.
 /// </summary>
 /// <param name="layout">The trap layout.</param>
 /// <param name="assemblyPath">The full path of the assembly to extract.</param>
 /// <param name="logger">The logger.</param>
 /// <param name="nocache">True to overwrite existing trap file.</param>
 /// <param name="extractPdbs">Whether to extract PDBs.</param>
 /// <param name="trapFile">The path of the trap file.</param>
 /// <param name="extracted">Whether the file was extracted (false=cached).</param>
 public static void ExtractCIL(string assemblyPath, ILogger logger, CommonOptions options, out string trapFile, out bool extracted)
 {
     trapFile  = "";
     extracted = false;
     try
     {
         var canonicalPathCache      = CanonicalPathCache.Create(logger, 1000);
         var pathTransformer         = new PathTransformer(canonicalPathCache);
         var extractor               = new TracingExtractor(assemblyPath, logger, pathTransformer, options);
         var transformedAssemblyPath = pathTransformer.Transform(assemblyPath);
         using var trapWriter = transformedAssemblyPath.WithSuffix(".cil").CreateTrapWriter(logger, options.TrapCompression, discardDuplicates: true);
         trapFile             = trapWriter.TrapFile;
         if (!options.Cache || !System.IO.File.Exists(trapFile))
         {
             ExtractCIL(extractor, trapWriter, options.PDB);
             extracted = true;
         }
     }
     catch (Exception ex)  // lgtm[cs/catch-of-all-exceptions]
     {
         logger.Log(Severity.Error, string.Format("Exception extracting {0}: {1}", assemblyPath, ex));
     }
 }
예제 #16
0
        public static void ExtractStandalone(
            IEnumerable <string> sources,
            IEnumerable <string> referencePaths,
            IProgressMonitor pm,
            ILogger logger,
            CommonOptions options)
        {
            var stopwatch = new Stopwatch();

            stopwatch.Start();

            var canonicalPathCache = CanonicalPathCache.Create(logger, 1000);
            var pathTransformer    = new PathTransformer(canonicalPathCache);

            using var analyser = new StandaloneAnalyser(pm, logger, false, pathTransformer);
            try
            {
                AnalyseStandalone(analyser, sources, referencePaths, options, pm, stopwatch);
            }
            catch (Exception ex)  // lgtm[cs/catch-of-all-exceptions]
            {
                analyser.Logger.Log(Severity.Error, "  Unhandled exception: {0}", ex);
            }
        }
예제 #17
0
        /// <summary>
        /// Command-line driver for the extractor.
        /// </summary>
        ///
        /// <remarks>
        /// The extractor can be invoked in one of two ways: Either as an "analyser" passed in via the /a
        /// option to csc.exe, or as a stand-alone executable. In this case, we need to faithfully
        /// drive Roslyn in the way that csc.exe would.
        /// </remarks>
        ///
        /// <param name="args">Command line arguments as passed to csc.exe</param>
        /// <returns><see cref="ExitCode"/></returns>
        public static ExitCode Run(string[] args)
        {
            var stopwatch = new Stopwatch();

            stopwatch.Start();

            Entities.Compilation.Settings = (Directory.GetCurrentDirectory(), args);

            var options    = Options.CreateWithEnvironment(Entities.Compilation.Settings.Args);
            var fileLogger = new FileLogger(options.Verbosity, GetCSharpLogPath());

            using var logger = options.Console
                ? new CombinedLogger(new ConsoleLogger(options.Verbosity), fileLogger)
                : (ILogger)fileLogger;

            if (Environment.GetEnvironmentVariable("SEMMLE_CLRTRACER") == "1" && !options.ClrTracer)
            {
                logger.Log(Severity.Info, "Skipping extraction since already extracted from the CLR tracer");
                return(ExitCode.Ok);
            }

            var canonicalPathCache = CanonicalPathCache.Create(logger, 1000);
            var pathTransformer    = new PathTransformer(canonicalPathCache);

            using var analyser = new TracingAnalyser(new LogProgressMonitor(logger), logger, options.AssemblySensitiveTrap, pathTransformer);

            try
            {
                if (options.ProjectsToLoad.Any())
                {
                    AddSourceFilesFromProjects(options.ProjectsToLoad, options.CompilerArguments, logger);
                }

                var compilerVersion = new CompilerVersion(options);

                if (compilerVersion.SkipExtraction)
                {
                    logger.Log(Severity.Warning, "  Unrecognized compiler '{0}' because {1}", compilerVersion.SpecifiedCompiler, compilerVersion.SkipReason);
                    return(ExitCode.Ok);
                }

                var compilerArguments = CSharpCommandLineParser.Default.Parse(
                    compilerVersion.ArgsWithResponse,
                    Entities.Compilation.Settings.Cwd,
                    compilerVersion.FrameworkPath,
                    compilerVersion.AdditionalReferenceDirectories
                    );

                if (compilerArguments is null)
                {
                    var sb = new StringBuilder();
                    sb.Append("  Failed to parse command line: ").AppendList(" ", Entities.Compilation.Settings.Args);
                    logger.Log(Severity.Error, sb.ToString());
                    ++analyser.CompilationErrors;
                    return(ExitCode.Failed);
                }

                if (!analyser.BeginInitialize(compilerVersion.ArgsWithResponse))
                {
                    logger.Log(Severity.Info, "Skipping extraction since files have already been extracted");
                    return(ExitCode.Ok);
                }

                return(AnalyseTracing(analyser, compilerArguments, options, canonicalPathCache, stopwatch));
            }
            catch (Exception ex)  // lgtm[cs/catch-of-all-exceptions]
            {
                logger.Log(Severity.Error, "  Unhandled exception: {0}", ex);
                return(ExitCode.Errors);
            }
        }
예제 #18
0
        public static void ExtractStandalone(
            IEnumerable <string> sources,
            IEnumerable <string> referencePaths,
            IProgressMonitor pm,
            ILogger logger,
            CommonOptions options)
        {
            var canonicalPathCache = CanonicalPathCache.Create(logger, 1000);
            var pathTransformer    = new PathTransformer(canonicalPathCache);

            using var analyser   = new Analyser(pm, logger, false, pathTransformer);
            using var references = new BlockingCollection <MetadataReference>();
            try
            {
                var referenceTasks = referencePaths.Select <string, Action>(path => () =>
                {
                    var reference = MetadataReference.CreateFromFile(path);
                    references.Add(reference);
                });

                var syntaxTrees     = new List <SyntaxTree>();
                var syntaxTreeTasks = ReadSyntaxTrees(sources, analyser, null, null, syntaxTrees);

                var sw = new Stopwatch();
                sw.Start();

                Parallel.Invoke(
                    new ParallelOptions {
                    MaxDegreeOfParallelism = options.Threads
                },
                    referenceTasks.Interleave(syntaxTreeTasks).ToArray());

                if (syntaxTrees.Count == 0)
                {
                    analyser.Logger.Log(Severity.Error, "  No source files");
                    ++analyser.CompilationErrors;
                }

                var compilation = CSharpCompilation.Create(
                    "csharp.dll",
                    syntaxTrees,
                    references
                    );

                analyser.InitializeStandalone(compilation, options);
                analyser.AnalyseReferences();

                foreach (var tree in compilation.SyntaxTrees)
                {
                    analyser.AnalyseTree(tree);
                }

                sw.Stop();
                analyser.Logger.Log(Severity.Info, "  Models constructed in {0}", sw.Elapsed);

                sw.Restart();
                analyser.PerformExtraction(options.Threads);
                sw.Stop();
                analyser.Logger.Log(Severity.Info, "  Extraction took {0}", sw.Elapsed);

                foreach (var type in analyser.MissingNamespaces)
                {
                    pm.MissingNamespace(type);
                }

                foreach (var type in analyser.MissingTypes)
                {
                    pm.MissingType(type);
                }

                pm.MissingSummary(analyser.MissingTypes.Count(), analyser.MissingNamespaces.Count());
            }
            catch (Exception ex)  // lgtm[cs/catch-of-all-exceptions]
            {
                analyser.Logger.Log(Severity.Error, "  Unhandled exception: {0}", ex);
            }
        }
예제 #19
0
        /// <summary>
        /// Command-line driver for the extractor.
        /// </summary>
        ///
        /// <remarks>
        /// The extractor can be invoked in one of two ways: Either as an "analyser" passed in via the /a
        /// option to csc.exe, or as a stand-alone executable. In this case, we need to faithfully
        /// drive Roslyn in the way that csc.exe would.
        /// </remarks>
        ///
        /// <param name="args">Command line arguments as passed to csc.exe</param>
        /// <returns><see cref="ExitCode"/></returns>
        public static ExitCode Run(string[] args)
        {
            var stopwatch = new Stopwatch();

            stopwatch.Start();
            var commandLineArguments = Options.CreateWithEnvironment(args);
            var fileLogger           = new FileLogger(commandLineArguments.Verbosity, GetCSharpLogPath());

            using var logger = commandLineArguments.Console
                ? new CombinedLogger(new ConsoleLogger(commandLineArguments.Verbosity), fileLogger)
                : (ILogger)fileLogger;

            if (Environment.GetEnvironmentVariable("SEMMLE_CLRTRACER") == "1" && !commandLineArguments.ClrTracer)
            {
                logger.Log(Severity.Info, "Skipping extraction since already extracted from the CLR tracer");
                return(ExitCode.Ok);
            }

            var canonicalPathCache = CanonicalPathCache.Create(logger, 1000);
            var pathTransformer    = new PathTransformer(canonicalPathCache);

            using var analyser   = new Analyser(new LogProgressMonitor(logger), logger, commandLineArguments.AssemblySensitiveTrap, pathTransformer);
            using var references = new BlockingCollection <MetadataReference>();
            try
            {
                var compilerVersion = new CompilerVersion(commandLineArguments);

                if (compilerVersion.SkipExtraction)
                {
                    logger.Log(Severity.Warning, "  Unrecognized compiler '{0}' because {1}", compilerVersion.SpecifiedCompiler, compilerVersion.SkipReason);
                    return(ExitCode.Ok);
                }

                var cwd = Directory.GetCurrentDirectory();
                var compilerArguments = CSharpCommandLineParser.Default.Parse(
                    compilerVersion.ArgsWithResponse,
                    cwd,
                    compilerVersion.FrameworkPath,
                    compilerVersion.AdditionalReferenceDirectories
                    );

                if (compilerArguments == null)
                {
                    var sb = new StringBuilder();
                    sb.Append("  Failed to parse command line: ").AppendList(" ", args);
                    logger.Log(Severity.Error, sb.ToString());
                    ++analyser.CompilationErrors;
                    return(ExitCode.Failed);
                }

                if (!analyser.BeginInitialize(compilerVersion.ArgsWithResponse))
                {
                    logger.Log(Severity.Info, "Skipping extraction since files have already been extracted");
                    return(ExitCode.Ok);
                }

                var referenceTasks = ResolveReferences(compilerArguments, analyser, canonicalPathCache, references);

                var syntaxTrees     = new List <SyntaxTree>();
                var syntaxTreeTasks = ReadSyntaxTrees(
                    compilerArguments.SourceFiles.
                    Select(src => canonicalPathCache.GetCanonicalPath(src.Path)),
                    analyser,
                    compilerArguments.ParseOptions,
                    compilerArguments.Encoding,
                    syntaxTrees);

                var sw1 = new Stopwatch();
                sw1.Start();

                Parallel.Invoke(
                    new ParallelOptions {
                    MaxDegreeOfParallelism = commandLineArguments.Threads
                },
                    referenceTasks.Interleave(syntaxTreeTasks).ToArray());

                if (syntaxTrees.Count == 0)
                {
                    logger.Log(Severity.Error, "  No source files");
                    ++analyser.CompilationErrors;
                    return(ExitCode.Failed);
                }

                // csc.exe (CSharpCompiler.cs) also provides CompilationOptions
                // .WithMetadataReferenceResolver(),
                // .WithXmlReferenceResolver() and
                // .WithSourceReferenceResolver().
                // These would be needed if we hadn't explicitly provided the source/references
                // already.
                var compilation = CSharpCompilation.Create(
                    compilerArguments.CompilationName,
                    syntaxTrees,
                    references,
                    compilerArguments.CompilationOptions.
                    WithAssemblyIdentityComparer(DesktopAssemblyIdentityComparer.Default).
                    WithStrongNameProvider(new DesktopStrongNameProvider(compilerArguments.KeyFileSearchPaths))
                    );

                analyser.EndInitialize(compilerArguments, commandLineArguments, compilation);
                analyser.AnalyseCompilation(cwd, args);
                analyser.AnalyseReferences();

                foreach (var tree in compilation.SyntaxTrees)
                {
                    analyser.AnalyseTree(tree);
                }

                var currentProcess = Process.GetCurrentProcess();
                var cpuTime1       = currentProcess.TotalProcessorTime;
                var userTime1      = currentProcess.UserProcessorTime;
                sw1.Stop();
                logger.Log(Severity.Info, "  Models constructed in {0}", sw1.Elapsed);

                var sw2 = new Stopwatch();
                sw2.Start();
                analyser.PerformExtraction(commandLineArguments.Threads);
                sw2.Stop();
                var cpuTime2  = currentProcess.TotalProcessorTime;
                var userTime2 = currentProcess.UserProcessorTime;

                var performance = new Entities.PerformanceMetrics()
                {
                    Frontend = new Entities.Timings()
                    {
                        Elapsed = sw1.Elapsed, Cpu = cpuTime1, User = userTime1
                    },
                    Extractor = new Entities.Timings()
                    {
                        Elapsed = sw2.Elapsed, Cpu = cpuTime2 - cpuTime1, User = userTime2 - userTime1
                    },
                    Total = new Entities.Timings()
                    {
                        Elapsed = stopwatch.Elapsed, Cpu = cpuTime2, User = userTime2
                    },
                    PeakWorkingSet = currentProcess.PeakWorkingSet64
                };

                analyser.LogPerformance(performance);
                logger.Log(Severity.Info, "  Extraction took {0}", sw2.Elapsed);

                return(analyser.TotalErrors == 0 ? ExitCode.Ok : ExitCode.Errors);
            }
            catch (Exception ex)  // lgtm[cs/catch-of-all-exceptions]
            {
                logger.Log(Severity.Error, "  Unhandled exception: {0}", ex);
                return(ExitCode.Errors);
            }
        }
        public static async Task <bool> ScanAndRepairFile(GameFileInfo fileInfo, string gameFilePath,
                                                          PathTransformer pathTransformer, IProgress <ScanSubProgress> progress = null,
                                                          int concurrentDownload = 0, CancellationToken ct = default)
        {
            var filePath = Path.Combine(gameFilePath, pathTransformer.TransformPath(fileInfo.FileName));

            //#1 Check File
            ct.ThrowIfCancellationRequested();
            progress?.Report(new ScanSubProgress(ScanSubProgressStep.Check, 0));

            Progress <double> subProgressCheck = null;

            if (progress != null)
            {
                subProgressCheck = new Progress <double>();
                subProgressCheck.ProgressChanged += (o, d) =>
                {
                    progress.Report(new ScanSubProgress(
                                        ScanSubProgressStep.Check, d));
                };
            }

            if (await RunFileCheck(filePath, fileInfo.Size, fileInfo.Crc32, ct, subProgressCheck))
            {
                progress?.Report(new ScanSubProgress(ScanSubProgressStep.End, 100));
                return(true);
            }

            //#2 Download File
            ct.ThrowIfCancellationRequested();
            progress?.Report(new ScanSubProgress(ScanSubProgressStep.Download, 0));

            var tempFileName = Path.Combine(GameScannerTempPath, $"{fileInfo.FileName.GetHashCode():X4}.tmp");

            if (File.Exists(tempFileName))
            {
                File.Delete(tempFileName);
            }

            var fileDownloader = concurrentDownload == 1
                ? (IFileDownloader) new SimpleFileDownloader(fileInfo.HttpLink, tempFileName)
                : new ChunkFileDownloader(fileInfo.HttpLink, tempFileName, GameScannerTempPath,
                                          concurrentDownload);

            if (progress != null)
            {
                fileDownloader.ProgressChanged += (sender, eventArg) =>
                {
                    switch (fileDownloader.State)
                    {
                    case FileDownloaderState.Invalid:
                    case FileDownloaderState.Download:
                        progress.Report(new ScanSubProgress(
                                            ScanSubProgressStep.Download, fileDownloader.DownloadProgress * 0.99,
                                            new ScanDownloadProgress(fileDownloader.DownloadSize, fileDownloader.BytesDownloaded,
                                                                     fileDownloader.DownloadSpeed)));
                        break;

                    case FileDownloaderState.Finalize:
                        progress.Report(new ScanSubProgress(
                                            ScanSubProgressStep.Download, 99,
                                            new ScanDownloadProgress(fileDownloader.DownloadSize, fileDownloader.BytesDownloaded,
                                                                     0)));
                        break;

                    case FileDownloaderState.Complete:
                        progress.Report(new ScanSubProgress(
                                            ScanSubProgressStep.Download, 100,
                                            new ScanDownloadProgress(fileDownloader.DownloadSize, fileDownloader.BytesDownloaded,
                                                                     0)));
                        break;

                    case FileDownloaderState.Error:
                    case FileDownloaderState.Abort:
                        break;

                    default:
                        throw new ArgumentOutOfRangeException(nameof(fileDownloader.State),
                                                              fileDownloader.State, null);
                    }
                }
            }
            ;

            await fileDownloader.DownloadAsync(ct);


            //#3 Check Downloaded File
            ct.ThrowIfCancellationRequested();

            Progress <double> subProgressCheckDown = null;

            if (progress != null)
            {
                progress.Report(new ScanSubProgress(ScanSubProgressStep.CheckDownload, 0));

                subProgressCheckDown = new Progress <double>();
                subProgressCheckDown.ProgressChanged += (o, d) =>
                {
                    progress.Report(new ScanSubProgress(
                                        ScanSubProgressStep.CheckDownload, d));
                };
            }

            try
            {
                await EnsureValidGameFile(tempFileName, fileInfo.BinSize, fileInfo.BinCrc32, ct, subProgressCheckDown);
            }
            catch
            {
                if (File.Exists(tempFileName))
                {
                    File.Delete(tempFileName);
                }

                throw;
            }

            //#4 Extract downloaded file
            ct.ThrowIfCancellationRequested();
            if (L33TZipUtils.IsL33TZipFile(tempFileName))
            {
                var tempFileName2 = $"{tempFileName.Replace(".tmp", string.Empty)}.ext.tmp";
                //
                Progress <double> extractProgress = null;
                if (progress != null)
                {
                    progress.Report(new ScanSubProgress(
                                        ScanSubProgressStep.ExtractDownload, 0));

                    extractProgress = new Progress <double>();
                    extractProgress.ProgressChanged += (o, d) =>
                    {
                        progress.Report(new ScanSubProgress(
                                            ScanSubProgressStep.ExtractDownload, d));
                    };
                }

                await L33TZipUtils.ExtractL33TZipFileAsync(tempFileName, tempFileName2, ct, extractProgress);

                //#4.1 Check Extracted File
                ct.ThrowIfCancellationRequested();
                Progress <double> subProgressCheckExt = null;
                if (progress != null)
                {
                    progress.Report(new ScanSubProgress(
                                        ScanSubProgressStep.CheckExtractDownload, 0));

                    subProgressCheckExt = new Progress <double>();
                    subProgressCheckExt.ProgressChanged += (o, d) =>
                    {
                        progress.Report(new ScanSubProgress(
                                            ScanSubProgressStep.CheckExtractDownload, d));
                    };
                }

                await EnsureValidGameFile(tempFileName2, fileInfo.Size, fileInfo.Crc32, ct, subProgressCheckExt);

                File.Delete(tempFileName);

                tempFileName = tempFileName2;
            }

            //#5 Move new file to game folder
            ct.ThrowIfCancellationRequested();

            progress?.Report(new ScanSubProgress(
                                 ScanSubProgressStep.Finalize, 0));

            if (File.Exists(filePath))
            {
                File.Delete(filePath);
            }
            else
            {
                var pathName = Path.GetDirectoryName(filePath);
                if (!string.IsNullOrEmpty(pathName) && !Directory.Exists(pathName))
                {
                    Directory.CreateDirectory(pathName);
                }
            }

            File.Move(tempFileName, filePath);

            //#6 End
            progress?.Report(new ScanSubProgress(
                                 ScanSubProgressStep.End, 100));

            return(true);
        }
 public TracingAnalyser(IProgressMonitor pm, ILogger logger, bool addAssemblyTrapPrefix, PathTransformer pathTransformer)
     : base(pm, logger, addAssemblyTrapPrefix, pathTransformer)
 {
 }