public async Task <SymbolTreeInfo> TryGetMetadataSymbolTreeInfoAsync( Solution solution, PortableExecutableReference reference, CancellationToken cancellationToken) { var checksum = SymbolTreeInfo.GetMetadataChecksum(solution, reference, cancellationToken); var key = GetReferenceKey(reference); if (key != null) { if (_metadataPathToInfo.TryGetValue(key, out var metadataInfo) && metadataInfo.SymbolTreeInfo.Checksum == checksum) { return(metadataInfo.SymbolTreeInfo); } } // If we didn't have it in our cache, see if we can load it from disk. // Note: pass 'loadOnly' so we only attempt to load from disk, not to actually // try to create the metadata. var info = await SymbolTreeInfo.GetInfoForMetadataReferenceAsync( solution, reference, checksum, loadOnly : true, cancellationToken : cancellationToken).ConfigureAwait(false); return(info); }
private async Task UpdateReferenceAsync( Project project, PortableExecutableReference reference, CancellationToken cancellationToken) { var key = GetReferenceKey(reference); if (key == null) { return; } var checksum = SymbolTreeInfo.GetMetadataChecksum(project.Solution, reference, cancellationToken); if (!_metadataPathToInfo.TryGetValue(key, out var metadataInfo) || metadataInfo.SymbolTreeInfo.Checksum != checksum) { var info = await SymbolTreeInfo.GetInfoForMetadataReferenceAsync( project.Solution, reference, checksum, loadOnly : false, cancellationToken : cancellationToken).ConfigureAwait(false); Contract.ThrowIfNull(info); Contract.ThrowIfTrue(info.Checksum != checksum, "If we computed a SymbolTreeInfo, then its checksum much match our checksum."); // Note, getting the info may fail (for example, bogus metadata). That's ok. // We still want to cache that result so that don't try to continuously produce // this info over and over again. metadataInfo = new MetadataInfo(info, metadataInfo.ReferencingProjects ?? new HashSet <ProjectId>()); _metadataPathToInfo[key] = metadataInfo; } // Keep track that this dll is referenced by this project. metadataInfo.ReferencingProjects.Add(project.Id); }
private async Task <ImmutableArray <IMethodSymbol>?> GetExtensionMethodSymbolsFromPeReferenceAsync( PortableExecutableReference peReference, bool forceIndexCreation, CancellationToken cancellationToken) { var index = await SymbolTreeInfo.GetInfoForMetadataReferenceAsync( Solution, peReference, loadOnly : !forceIndexCreation, cancellationToken).ConfigureAwait(false); if (index == null) { // Returns null to indicate index not ready return(null); } if (!(index.ContainsExtensionMethod && _originatingSemanticModel.Compilation.GetAssemblyOrModuleSymbol(peReference) is IAssemblySymbol assembly)) { return(ImmutableArray <IMethodSymbol> .Empty); } var filter = CreateAggregatedFilter(index); var internalsVisible = _originatingSemanticModel.Compilation.Assembly.IsSameAssemblyOrHasFriendAccessTo(assembly); var matchingMethodSymbols = GetPotentialMatchingSymbolsFromAssembly(assembly, filter, internalsVisible, cancellationToken); return(GetExtensionMethodsForSymbolsFromSameCompilation(matchingMethodSymbols, cancellationToken)); }
private async Task UpdateReferenceAsync( Project project, Compilation compilation, PortableExecutableReference reference, CancellationToken cancellationToken) { var key = GetReferenceKey(reference); if (key == null) { return; } DateTime lastWriteTime; if (!TryGetLastWriteTime(key, out lastWriteTime)) { // Couldn't get the write time. Just ignore this reference. return; } MetadataInfo metadataInfo; if (!_metadataPathToInfo.TryGetValue(key, out metadataInfo) || metadataInfo.TimeStamp == lastWriteTime) { var info = await SymbolTreeInfo.GetInfoForMetadataReferenceAsync( project.Solution, reference, loadOnly : false, cancellationToken : cancellationToken).ConfigureAwait(false); metadataInfo = new MetadataInfo(lastWriteTime, info, metadataInfo.ReferencingProjects ?? new HashSet <ProjectId>()); _metadataPathToInfo.AddOrUpdate(key, metadataInfo, (_1, _2) => metadataInfo); } // Keep track that this dll is referenced by this project. metadataInfo.ReferencingProjects.Add(project.Id); }
public async Task <SymbolTreeInfo> TryGetMetadataSymbolTreeInfoAsync( Solution solution, PortableExecutableReference reference, CancellationToken cancellationToken) { var metadataId = SymbolTreeInfo.GetMetadataIdNoThrow(reference); if (metadataId == null) { return(null); } var checksum = SymbolTreeInfo.GetMetadataChecksum(solution, reference, cancellationToken); // See if the last value produced matches what the caller is asking for. If so, return that. if (_metadataIdToInfo.TryGetValue(metadataId, out var metadataInfo) && metadataInfo.SymbolTreeInfo.Checksum == checksum) { return(metadataInfo.SymbolTreeInfo); } // If we didn't have it in our cache, see if we can load it from disk. // Note: pass 'loadOnly' so we only attempt to load from disk, not to actually // try to create the metadata. var info = await SymbolTreeInfo.GetInfoForMetadataReferenceAsync( solution, reference, checksum, loadOnly : true, cancellationToken).ConfigureAwait(false); return(info); }
private static async Task <GetIndicesResult> TryGetIndicesAsync( Project currentProject, bool forceIndexCreation, CancellationToken cancellationToken) { var solution = currentProject.Solution; var cacheService = GetCacheService(solution.Workspace); var graph = currentProject.Solution.GetProjectDependencyGraph(); var relevantProjectIds = graph.GetProjectsThatThisProjectTransitivelyDependsOn(currentProject.Id) .Concat(currentProject.Id); using var syntaxDisposer = ArrayBuilder <CacheEntry> .GetInstance(out var syntaxBuilder); using var symbolDisposer = ArrayBuilder <SymbolTreeInfo> .GetInstance(out var symbolBuilder); foreach (var projectId in relevantProjectIds) { var project = solution.GetProject(projectId); if (project == null || !project.SupportsCompilation) { continue; } // By default, don't trigger index creation except for documents in current project. var loadOnly = !forceIndexCreation && projectId != currentProject.Id; var cacheEntry = await GetCacheEntryAsync(project, loadOnly, cacheService, cancellationToken).ConfigureAwait(false); if (cacheEntry == null) { // Don't provide anything if we don't have all the required SyntaxTreeIndex created. return(GetIndicesResult.NoneResult); } syntaxBuilder.Add(cacheEntry.Value); } // Search through all direct PE references. foreach (var peReference in currentProject.MetadataReferences.OfType <PortableExecutableReference>()) { var info = await SymbolTreeInfo.GetInfoForMetadataReferenceAsync( solution, peReference, loadOnly : !forceIndexCreation, cancellationToken).ConfigureAwait(false); if (info == null) { // Don't provide anything if we don't have all the required SymbolTreeInfo created. return(GetIndicesResult.NoneResult); } if (info.ContainsExtensionMethod) { symbolBuilder.Add(info); } } var syntaxIndices = syntaxBuilder.ToImmutable(); var symbolInfos = symbolBuilder.ToImmutable(); return(new GetIndicesResult(hasResult: true, syntaxIndices, symbolInfos)); }
static async Task UpdateReferenceAsync( ConcurrentDictionary <MetadataId, MetadataInfo> metadataIdToInfo, Project project, PortableExecutableReference reference, CancellationToken cancellationToken ) { var metadataId = SymbolTreeInfo.GetMetadataIdNoThrow(reference); if (metadataId == null) { return; } // 🐉 PERF: GetMetadataChecksum indirectly uses a ConditionalWeakTable. This call is intentionally // placed before the first 'await' of this asynchronous method to ensure it executes in the // synchronous portion of the caller. https://dev.azure.com/devdiv/DevDiv/_workitems/edit/1270250 var checksum = SymbolTreeInfo.GetMetadataChecksum( project.Solution, reference, cancellationToken ); if ( !metadataIdToInfo.TryGetValue(metadataId, out var metadataInfo) || metadataInfo.SymbolTreeInfo.Checksum != checksum ) { var info = await SymbolTreeInfo .GetInfoForMetadataReferenceAsync( project.Solution, reference, checksum, loadOnly : false, cancellationToken : cancellationToken ) .ConfigureAwait(false); Contract.ThrowIfNull(info); Contract.ThrowIfTrue( info.Checksum != checksum, "If we computed a SymbolTreeInfo, then its checksum much match our checksum." ); // Note, getting the info may fail (for example, bogus metadata). That's ok. // We still want to cache that result so that don't try to continuously produce // this info over and over again. metadataInfo = new MetadataInfo( info, metadataInfo.ReferencingProjects ?? new HashSet <ProjectId>() ); metadataIdToInfo[metadataId] = metadataInfo; } // Keep track that this dll is referenced by this project. lock (metadataInfo.ReferencingProjects) { metadataInfo.ReferencingProjects.Add(project.Id); } }
public static async ValueTask UpdateCacheAsync(Project project, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); var cacheService = GetCacheService(project); foreach (var relevantProject in GetAllRelevantProjects(project)) { await GetUpToDateCacheEntryAsync(relevantProject, cacheService, cancellationToken).ConfigureAwait(false); } foreach (var peReference in GetAllRelevantPeReferences(project)) { await SymbolTreeInfo.GetInfoForMetadataReferenceAsync(project.Solution, peReference, loadOnly : false, cancellationToken).ConfigureAwait(false); } }
public Task PopulateIndicesAsync(CancellationToken cancellationToken) { using var _ = ArrayBuilder <Task> .GetInstance(out var tasks); foreach (var project in GetAllRelevantProjects()) { tasks.Add(Task.Run(() => GetCacheEntryAsync(project, loadOnly: false, _cacheService, cancellationToken), cancellationToken)); } foreach (var peReference in GetAllRelevantPeReferences()) { tasks.Add(Task.Run(() => SymbolTreeInfo.GetInfoForMetadataReferenceAsync(Solution, peReference, loadOnly: false, cancellationToken), cancellationToken)); } return(Task.WhenAll(tasks.ToImmutable())); }
private async ValueTask <ImmutableArray <IMethodSymbol>?> GetExtensionMethodSymbolsFromPeReferenceAsync( PortableExecutableReference peReference, bool forceCacheCreation, CancellationToken cancellationToken) { SymbolTreeInfo?symbolInfo; if (forceCacheCreation) { symbolInfo = await SymbolTreeInfo.GetInfoForMetadataReferenceAsync( _originatingDocument.Project.Solution, peReference, loadOnly : false, cancellationToken).ConfigureAwait(false); } else { var cachedInfoTask = SymbolTreeInfo.TryGetCachedInfoForMetadataReferenceIgnoreChecksumAsync(peReference, cancellationToken); if (cachedInfoTask.IsCompleted) { // Use cached data if available, even checksum doesn't match. We will update the cache in the background. symbolInfo = await cachedInfoTask.ConfigureAwait(false); } else { // No cached data immediately available, returns null to indicate index not ready return(null); } } if (symbolInfo is null || !symbolInfo.ContainsExtensionMethod || _originatingSemanticModel.Compilation.GetAssemblyOrModuleSymbol(peReference) is not IAssemblySymbol assembly) { return(ImmutableArray <IMethodSymbol> .Empty); } var filter = CreateAggregatedFilter(symbolInfo); var internalsVisible = _originatingSemanticModel.Compilation.Assembly.IsSameAssemblyOrHasFriendAccessTo(assembly); var matchingMethodSymbols = GetPotentialMatchingSymbolsFromAssembly(assembly, filter, internalsVisible, cancellationToken); return(GetExtensionMethodsForSymbolsFromSameCompilation(matchingMethodSymbols, cancellationToken)); }
static async Task PopulateIndicesAsync(Project currentProject, CancellationToken cancellationToken) { var solution = currentProject.Solution; var cacheService = GetCacheService(solution.Workspace); using var _ = ArrayBuilder <Task> .GetInstance(out var tasks); foreach (var project in GetAllRelevantProjects(currentProject)) { tasks.Add(Task.Run(() => GetCacheEntryAsync(project, loadOnly: false, cacheService, cancellationToken), cancellationToken)); } foreach (var peReference in GetAllRelevantPeReferences(currentProject)) { tasks.Add(Task.Run(() => SymbolTreeInfo.GetInfoForMetadataReferenceAsync(solution, peReference, loadOnly: false, cancellationToken), cancellationToken)); } await Task.WhenAll(tasks.ToImmutable()).ConfigureAwait(false); }
private static async Task FindImmediateMatchingMetadataTypesInMetadataReferenceAsync( SymbolAndProjectIdSet metadataTypes, Project project, Func <SymbolAndProjectIdSet, INamedTypeSymbol, bool> metadataTypeMatches, Compilation compilation, PortableExecutableReference reference, SymbolAndProjectIdSet result, CancellationToken cancellationToken) { // We store an index in SymbolTreeInfo of the *simple* metadata type name // to the names of the all the types that either immediately derive or // implement that type. Because the mapping is from the simple name // we might get false positives. But that's fine as we still use // 'metadataTypeMatches' to make sure the match is correct. var symbolTreeInfo = await SymbolTreeInfo.GetInfoForMetadataReferenceAsync( project.Solution, reference, loadOnly : false, cancellationToken : cancellationToken).ConfigureAwait(false); // For each type we care about, see if we can find any derived types // in this index. foreach (var metadataType in metadataTypes) { var baseTypeName = metadataType.Symbol.Name; // For each derived type we find, see if we can map that back // to an actual symbol. Then check if that symbol actually fits // our criteria. foreach (var derivedType in symbolTreeInfo.GetDerivedMetadataTypes(baseTypeName, compilation, cancellationToken)) { if (derivedType != null && derivedType.Locations.Any(s_isInMetadata)) { if (metadataTypeMatches(metadataTypes, derivedType)) { result.Add(SymbolAndProjectId.Create(derivedType, project.Id)); } } } } }
/// <summary> /// Force create all relevant indices /// </summary> public static Task PopulateIndicesAsync(Project?project, IImportCompletionCacheService <CacheEntry, object> cacheService, CancellationToken cancellationToken) { if (project is null) { return(Task.CompletedTask); } using var _ = ArrayBuilder <Task> .GetInstance(out var tasks); foreach (var relevantProject in GetAllRelevantProjects(project)) { tasks.Add(Task.Run(() => GetCacheEntryAsync(relevantProject, loadOnly: false, cacheService, cancellationToken), cancellationToken)); } foreach (var peReference in GetAllRelevantPeReferences(project)) { tasks.Add(Task.Run(() => SymbolTreeInfo.GetInfoForMetadataReferenceAsync(project.Solution, peReference, loadOnly: false, cancellationToken).AsTask(), cancellationToken)); } return(Task.WhenAll(tasks.ToImmutable())); }