예제 #1
0
    public static async Task <CrawlerResults> CrawlAsync(NuGetFeed feed, PackageIdentity packageId)
    {
        ArgumentNullException.ThrowIfNull(feed);
        ArgumentNullException.ThrowIfNull(packageId);

        var crawler = new AssemblyCrawler();

        using var reader = await feed.GetPackageAsync(packageId).ConfigureAwait(false);

        foreach (var packagePath in reader.GetFiles())
        {
#pragma warning disable CA2007 // Consider calling ConfigureAwait on the awaited task
            await using var assemblyStream = reader.GetStream(packagePath);
            await using var memoryStream   = new MemoryStream();
#pragma warning restore CA2007 // Consider calling ConfigureAwait on the awaited task
            await assemblyStream.CopyToAsync(memoryStream).ConfigureAwait(false);

            memoryStream.Position = 0;

            using var env = new HostEnvironment();
            var assembly = env.LoadAssemblyFrom(packagePath, memoryStream);
            if (assembly is null)
            {
                continue;
            }

            crawler.Crawl(assembly);
        }

        return(crawler.CreateResults());
    }
예제 #2
0
    public static async Task <CrawlerResults> CrawlAsync(NuGetFeed feed, PackageIdentity packageId)
    {
        var crawler = new AssemblyCrawler();
        var reader  = await feed.GetPackageAsync(packageId);

        foreach (var packagePath in reader.GetFiles())
        {
            await using var assemblyStream = reader.GetStream(packagePath);
            await using var memoryStream   = new MemoryStream();
            await assemblyStream.CopyToAsync(memoryStream);

            memoryStream.Position = 0;

            var env      = new HostEnvironment();
            var assembly = env.LoadAssemblyFrom(packagePath, memoryStream);
            if (assembly is null)
            {
                continue;
            }

            crawler.Crawl(assembly);
        }

        return(crawler.GetResults());
    }
예제 #3
0
    private static void Check(IAssembly assembly, IReadOnlyDictionary <string, int> expectedResultsText)
    {
        var expectedResults = expectedResultsText.ToDictionary(kv => new ApiKey(kv.Key), kv => kv.Value);

        var crawler = new AssemblyCrawler();

        crawler.Crawl(assembly);

        var actualResults = crawler.GetResults().Data;

        var messageBuilder = new StringBuilder();

        foreach (var key in expectedResults.Keys.Where(k => actualResults.ContainsKey(k) && expectedResults[k] != actualResults[k]))
        {
            if (_autoGeneratedKeys.Contains(key.DocumentationId))
            {
                continue;
            }

            messageBuilder.AppendLine($"{key} was expected to have a value {expectedResults[key]} but was {actualResults[key]}.");
        }

        foreach (var key in expectedResults.Keys.Where(k => !actualResults.ContainsKey(k)))
        {
            if (_autoGeneratedKeys.Contains(key.DocumentationId))
            {
                continue;
            }

            messageBuilder.AppendLine($"{key} was expected but is missing.");
        }

        foreach (var key in actualResults.Keys.Where(k => !expectedResults.ContainsKey(k)))
        {
            if (_autoGeneratedKeys.Contains(key.DocumentationId))
            {
                continue;
            }

            messageBuilder.AppendLine($"{key} was not expected.");
        }

        if (messageBuilder.Length > 0)
        {
            throw new Exception(messageBuilder.ToString());
        }
    }
예제 #4
0
    public static void Run(ApiCatalogModel catalog,
                           IReadOnlyCollection <string> filePaths,
                           IReadOnlyList <NuGetFramework> frameworks,
                           bool analyzeObsoletion,
                           IReadOnlyList <string> platforms,
                           Action <AssemblyResult> resultReceiver)
    {
        var apiByGuid           = catalog.GetAllApis().ToDictionary(a => a.Guid);
        var availabilityContext = ApiAvailabilityContext.Create(catalog);

        var platformContexts = frameworks.Select(fx => PlatformAnnotationContext.Create(availabilityContext, fx.GetShortFolderName()))
                               .ToDictionary(pc => pc.Framework);

        foreach (var api in catalog.GetAllApis())
        {
            var forwardedApi = catalog.GetForwardedApi(api);
            if (forwardedApi is not null)
            {
                apiByGuid[api.Guid] = forwardedApi.Value;
            }
        }

        var apiAvailability = new ConcurrentDictionary <ApiModel, ApiAvailability>();

        var resultSink = new BlockingCollection <AssemblyResult>();

        var resultSinkTask = Task.Run(() =>
        {
            foreach (var result in resultSink.GetConsumingEnumerable())
            {
                resultReceiver(result);
            }
        });

        Parallel.ForEach(filePaths, filePath =>
        {
            using var env    = new HostEnvironment();
            var assembly     = env.LoadAssemblyFrom(filePath);
            var assemblyName = assembly is not null
                                ? assembly.Name.Value
                                : Path.GetFileName(filePath);
            if (assembly is null)
            {
                var result = new AssemblyResult(assemblyName, "Not a valid .NET assembly", Array.Empty <ApiResult>());
                resultSink.Add(result);
            }
            else
            {
                var assemblyTfm       = assembly.GetTargetFrameworkMoniker();
                var assemblyFramework = string.IsNullOrEmpty(assemblyTfm) ? null : NuGetFramework.Parse(assemblyTfm);

                var crawler = new AssemblyCrawler();
                crawler.Crawl(assembly);

                var crawlerResults = crawler.GetResults();

                var apiResults             = new List <ApiResult>();
                var frameworkResultBuilder = new List <FrameworkResult>(frameworks.Count);
                var platformResultBuilder  = new List <PlatformResult?>(platforms.Count);

                foreach (var apiKey in crawlerResults.Data.Keys)
                {
                    if (apiByGuid.TryGetValue(apiKey.Guid, out var api))
                    {
                        var availability = apiAvailability.GetOrAdd(api, a => availabilityContext.GetAvailability(a));

                        frameworkResultBuilder.Clear();

                        foreach (var framework in frameworks)
                        {
                            // Analyze availability

                            AvailabilityResult availabilityResult;

                            var infos = availability.Frameworks.Where(fx => fx.Framework == framework).ToArray();

                            // NOTE: There are APIs that exist in multiple places in-box, e.g. Microsoft.Windows.Themes.ListBoxChrome.
                            //       It doesn't really matter for our purposes. Either way, we'll pick the first one.
                            var info = infos.FirstOrDefault(i => i.IsInBox) ?? infos.FirstOrDefault(i => !i.IsInBox);

                            if (info is null)
                            {
                                availabilityResult = AvailabilityResult.Unavailable;
                            }
                            else if (info.IsInBox)
                            {
                                availabilityResult = AvailabilityResult.AvailableInBox;
                            }
                            else
                            {
                                availabilityResult = AvailabilityResult.AvailableInPackage(info.Package.Value);
                            }

                            // Analyze obsoletion

                            ObsoletionResult?obsoletionResult;

                            if (!analyzeObsoletion || info?.Declaration.Obsoletion is null)
                            {
                                obsoletionResult = null;
                            }
                            else
                            {
                                var compiledAgainstObsoleteApi = false;

                                if (assemblyFramework is not null)
                                {
                                    var compiledAvailability = availabilityContext.GetAvailability(api, assemblyFramework);
                                    if (compiledAvailability?.Declaration.Obsoletion is not null)
                                    {
                                        compiledAgainstObsoleteApi = true;
                                    }
                                }

                                if (compiledAgainstObsoleteApi)
                                {
                                    obsoletionResult = null;
                                }
                                else
                                {
                                    var o            = info.Declaration.Obsoletion.Value;
                                    obsoletionResult = new ObsoletionResult(o.Message, o.Url);
                                }
                            }

                            // Analyze platform support

                            platformResultBuilder.Clear();

                            if (info is null)
                            {
                                for (var i = 0; i < platforms.Count; i++)
                                {
                                    platformResultBuilder.Add(null);
                                }
                            }
                            else
                            {
                                var platformContext = platformContexts[framework];
                                foreach (var platform in platforms)
                                {
                                    var annotation     = platformContext.GetPlatformAnnotation(api);
                                    var isSupported    = annotation.IsSupported(platform);
                                    var platformResult = isSupported ? PlatformResult.Supported : PlatformResult.Unsupported;
                                    platformResultBuilder.Add(platformResult);
                                }
                            }

                            var frameworkResult = new FrameworkResult(availabilityResult, obsoletionResult, platformResultBuilder.ToArray());
                            frameworkResultBuilder.Add(frameworkResult);
                        }

                        var apiResult = new ApiResult(api, frameworkResultBuilder.ToArray());
                        apiResults.Add(apiResult);
                    }
                }

                var results = new AssemblyResult(assemblyName, null, apiResults.ToArray());
                resultSink.Add(results);
            }
        });

        resultSink.CompleteAdding();
        resultSinkTask.Wait();
    }
예제 #5
0
        public async Task RunAsync(Func <AnalyzeResult, Task> receiver)
        {
            var framework = NuGetFramework.ParseFolder(_tfmSelector.DetermineTargetTfmValue());
            var platforms = _options.Platform.Select(p => p.ToString().ToLowerInvariant()).ToImmutableList();

            async void Receiver(AssemblyResult result)
            {
                _logger.LogInformation(Resources.BinaryAnalysisProcessedAssemblyMessageFormat, result.AssemblyName);

                foreach (var r in CreateUaResults(result).ToList())
                {
                    await receiver(r).ConfigureAwait(false);
                }
            }

            var catalog = await CatalogService.LoadCatalogAsync().ConfigureAwait(false);

            var apiByGuid           = catalog.GetAllApis().ToDictionary(a => a.UniqueId);
            var availabilityContext = ApiAvailabilityContext.Create(catalog);

            var platformContext = PlatformAnnotationContext.Create(availabilityContext, framework.GetShortFolderName());

            foreach (var api in catalog.GetAllApis())
            {
                var forwardedApi = catalog.GetForwardedApi(api);
                if (forwardedApi is not null)
                {
                    apiByGuid[api.UniqueId] = forwardedApi.Value;
                }
            }

            var apiAvailability = new ConcurrentDictionary <ApiModel, ApiAvailability>();

            using var resultSink = new BlockingCollection <AssemblyResult>();

            var resultSinkTask = Task.Run(() =>
            {
                foreach (var result in resultSink.GetConsumingEnumerable())
                {
                    Receiver(result);
                }
            });

            var filePaths = AssemblyFileSet.Create(_options.Content.ToList());

            Parallel.ForEach(filePaths, filePath =>
            {
                using var env    = new HostEnvironment();
                var assembly     = env.LoadAssemblyFrom(filePath);
                var assemblyName = assembly is not null
                                    ? assembly.Name.Value
                                    : Path.GetFileName(filePath);
                if (assembly is null)
                {
                    var result = new AssemblyResult(assemblyName, Resources.BinaryAnalysisInvalidAssemblyMessage, Array.Empty <ApiResult>());
                    resultSink.Add(result);
                }
                else
                {
                    var assemblyTfm       = assembly.GetTargetFrameworkMoniker();
                    var assemblyFramework = string.IsNullOrEmpty(assemblyTfm) ? null : NuGetFramework.Parse(assemblyTfm);

                    var crawler = new AssemblyCrawler();
                    crawler.Crawl(assembly);

                    var crawlerResults = crawler.CreateResults();

                    var apiResults             = new List <ApiResult>();
                    var frameworkResultBuilder = new List <FrameworkResult>();
                    var platformResultBuilder  = new List <PlatformResult?>(platforms.Count);

                    foreach (var apiKey in crawlerResults.Data.Keys)
                    {
                        if (apiByGuid.TryGetValue(apiKey.Id, out var api))
                        {
                            var availability = apiAvailability.GetOrAdd(api, a => availabilityContext.GetAvailability(a));

                            frameworkResultBuilder.Clear();

                            AnalyzeAvailability(availability, framework, out AvailabilityResult availabilityResult, out ApiFrameworkAvailability? info);

                            var obsoletionResult = AnalyzeObsoletion(availabilityContext, assemblyFramework, api, info);

                            AnalyzePlatformSupport(platforms, platformContext, frameworkResultBuilder, platformResultBuilder !, api, framework !, availabilityResult, info, obsoletionResult);

                            var apiResult = new ApiResult(api, frameworkResultBuilder.ToArray());
                            apiResults.Add(apiResult);
                        }