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()); }
public async Task InvalidateAsync() { if (!_environment.IsDevelopment()) { File.Delete(GetDatabasePath()); File.Delete(GetSuffixTreePath()); } var azureConnectionString = _configuration["AzureStorageConnectionString"]; var databasePath = GetDatabasePath(); if (!File.Exists(databasePath)) { var blobClient = new BlobClient(azureConnectionString, "catalog", "apicatalog.dat"); await blobClient.DownloadToAsync(databasePath); } var catalog = await ApiCatalogModel.LoadAsync(databasePath); var availabilityContext = ApiAvailabilityContext.Create(catalog); var apiByGuid = catalog.GetAllApis().ToDictionary(a => a.Guid); var suffixTreePath = GetSuffixTreePath(); if (!File.Exists(suffixTreePath)) { // TODO: Ideally the underlying file format uses compression. This seems weird. var blobClient = new BlobClient(azureConnectionString, "catalog", "suffixtree.dat.deflate"); using var blobStream = await blobClient.OpenReadAsync(); using var deflateStream = new DeflateStream(blobStream, CompressionMode.Decompress); using var fileStream = File.Create(suffixTreePath); await deflateStream.CopyToAsync(fileStream); } var suffixTree = SuffixTree.Load(suffixTreePath); var jobBlobClient = new BlobClient(azureConnectionString, "catalog", "job.json"); using var jobStream = await jobBlobClient.OpenReadAsync(); var jobInfo = await JsonSerializer.DeserializeAsync <CatalogJobInfo>(jobStream); _catalog = catalog; _availabilityContext = availabilityContext; _statistics = catalog.GetStatistics(); _apiByGuid = apiByGuid; _suffixTree = suffixTree; _jobInfo = jobInfo; }
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()); }
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(); }
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); }