Пример #1
0
    public async Task PlatformContext_Event()
    {
        var source = @"
            using System;
            using System.Runtime.Versioning;
            namespace System
            {
                public class TheClass
                {
                    [SupportedOSPlatform(""ios"")]
                    public event EventHandler TheEvent;
                }
            }
        ";

        var expectedPlatforms = @"
            The API is only supported on these platforms:
            - iOS
        ";

        var catalog = await new FluentCatalogBuilder()
                      .AddFramework("net45", fx =>
                                    fx.AddAssembly("System.TheAssembly", source))
                      .BuildAsync();

        var availabilityContext = ApiAvailabilityContext.Create(catalog);
        var context             = PlatformAnnotationContext.Create(availabilityContext, "net45");
        var api        = Assert.Single(catalog.GetAllApis().Where(a => a.GetFullName() == "System.TheClass.TheEvent.TheEvent.add"));
        var annotation = context.GetPlatformAnnotation(api);

        Assert.True(annotation.IsSupported("ios"));
        Assert.False(annotation.IsSupported("windows"));

        Assert.Equal(expectedPlatforms.Unindent(), annotation.ToString().Trim());
    }
Пример #2
0
    public override async Task ExecuteAsync()
    {
        var zero = new Version(0, 0, 0, 0);

        var catalog = await _catalogService.LoadCatalogAsync();

        var platforms = catalog.Platforms.Select(p => PlatformAnnotationContext.ParsePlatform(p.Name))
                        .GroupBy(p => p.Name, p => p.Version)
                        .Select(g => (Name: g.Key, Min: g.Where(v => v != zero).DefaultIfEmpty(zero).Min(), Max: g.Max()))
                        .OrderBy(n => n.Name);

        Console.WriteLine("Available platforms:");

        foreach (var platform in platforms)
        {
            if (platform.Min == zero && platform.Max == zero)
            {
                Console.WriteLine($"  {platform.Name}");
            }
            else
            {
                Console.WriteLine($"  {platform.Name,-25}{Prettify(platform.Min)}-{Prettify(platform.Max)}");
            }
        }

        string Prettify(Version?version)
        {
            if (version is null)
            {
                return(string.Empty);
            }

            if (version.Revision == 0)
            {
                if (version.Build == 0)
                {
                    return($"{version.Major}.{version.Minor}");
                }

                return($"{version.Major}.{version.Minor}.{version.Build}");
            }

            return($"{version.Major}.{version.Minor}.{version.Build}.{version.Revision}");
        }
    }
Пример #3
0
    private async Task UpdateSyntaxAsync()
    {
        // TODO: Handle invalid GUID
        // TODO: Handle API not found

        Api                  = CatalogService.GetApiByGuid(System.Guid.Parse(Guid));
        Availability         = CatalogService.AvailabilityContext.GetAvailability(Api);
        SelectedFramework    = SelectFramework(Availability, NavigationManager.GetQueryParameter("fx"));
        SelectedAvailability = Availability.Frameworks.FirstOrDefault(fx => fx.Framework.GetShortFolderName() == SelectedFramework) ??
                               Availability.Frameworks.FirstOrDefault();
        PlatformAnnotationContext = PlatformAnnotationContext.Create(CatalogService.AvailabilityContext, SelectedFramework);
        PreviewRequirement        = GetPreviewRequirement();

        Breadcrumbs = Api.AncestorsAndSelf().Reverse();

        if (Api.Kind.IsMember() && Api.Parent is not null)
        {
            Parent = Api.Parent.Value;
        }
        else
        {
            Parent = Api;
        }

        SelectedMarkup = SelectedAvailability?.Declaration.GetMarkup();

        var helpLink = Api.GetHelpLink();

        using var httpClient = new HttpClient();
        using var response   = await httpClient.GetAsync(helpLink);

        if (response.StatusCode == HttpStatusCode.OK)
        {
            HelpLink = helpLink;
        }
        else
        {
            HelpLink = null;
        }
    }
Пример #4
0
    public async Task PlatformContext_DenyList()
    {
        var source = @"
            using System.Runtime.Versioning;
            namespace System
            {
                [UnsupportedOSPlatform(""ios"")]
                [UnsupportedOSPlatform(""tvos"")]
                public class TheClass { }
            }
        ";

        var expectedPlatforms = @"
            The API is supported on any platform except for:
            - iOS
            - Mac Catalyst
            - tvOS
        ";

        var catalog = await new FluentCatalogBuilder()
                      .AddFramework("net45", fx =>
                                    fx.AddAssembly("System.Runtime", GetOperatingSystemWithImpliedPlatform())
                                    .AddAssembly("System.TheAssembly", source))
                      .BuildAsync();

        var availabilityContext = ApiAvailabilityContext.Create(catalog);
        var context             = PlatformAnnotationContext.Create(availabilityContext, "net45");
        var api        = Assert.Single(catalog.GetAllApis().Where(a => a.GetFullName() == "System.TheClass"));
        var annotation = context.GetPlatformAnnotation(api);

        Assert.False(annotation.IsSupported("ios"));
        Assert.False(annotation.IsSupported("tvos"));
        Assert.False(annotation.IsSupported("maccatalyst"));
        Assert.True(annotation.IsSupported("windows"));

        Assert.Equal(expectedPlatforms.Unindent(), annotation.ToString().Trim());
    }
Пример #5
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();
    }
Пример #6
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);
                        }