提交 8dae01b8 编写于 作者: C CyrusNajmabadi 提交者: GitHub

Merge pull request #19408 from CyrusNajmabadi/nullMetadataInfo

Do not pass around null SymbolTreeIndices.  Instead, pass around empty ones.
......@@ -4,6 +4,7 @@
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Composition;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
......@@ -39,8 +40,8 @@ internal class SymbolTreeInfoIncrementalAnalyzerProvider : IIncrementalAnalyzerP
private struct MetadataInfo
{
/// <summary>
/// Note: can be <code>null</code> if were unable to create a SymbolTreeInfo
/// (for example, if the metadata was bogus and we couldn't read it in).
/// Can't be null. Even if we weren't able to read in metadata, we'll still create an empty
/// index.
/// </summary>
public readonly SymbolTreeInfo SymbolTreeInfo;
......@@ -53,6 +54,7 @@ private struct MetadataInfo
public MetadataInfo(SymbolTreeInfo info, HashSet<ProjectId> referencingProjects)
{
Contract.ThrowIfNull(info);
SymbolTreeInfo = info;
ReferencingProjects = referencingProjects;
}
......@@ -123,7 +125,7 @@ private class SymbolTreeInfoCacheService : ISymbolTreeInfoCacheService
// 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.TryGetInfoForMetadataReferenceAsync(
var info = await SymbolTreeInfo.GetInfoForMetadataReferenceAsync(
solution, reference, checksum, loadOnly: true, cancellationToken: cancellationToken).ConfigureAwait(false);
return info;
}
......@@ -194,11 +196,6 @@ private async Task UpdateSymbolTreeInfoAsync(Project project, CancellationToken
return;
}
if (!project.SupportsCompilation)
{
return;
}
// Produce the indices for the source and metadata symbols in parallel.
var projectTask = UpdateSourceSymbolTreeInfoAsync(project, cancellationToken);
var referencesTask = UpdateReferencesAync(project, cancellationToken);
......@@ -215,6 +212,9 @@ private async Task UpdateSourceSymbolTreeInfoAsync(Project project, Cancellation
projectInfo = await SymbolTreeInfo.GetInfoForSourceAssemblyAsync(
project, checksum, cancellationToken).ConfigureAwait(false);
Contract.ThrowIfNull(projectInfo);
Contract.ThrowIfTrue(projectInfo.Checksum != checksum, "If we computed a SymbolTreeInfo, then its checksum much match our checksum.");
// Mark that we're up to date with this project. Future calls with the same
// semantic version can bail out immediately.
_projectToInfo.AddOrUpdate(project.Id, projectInfo, (_1, _2) => projectInfo);
......@@ -244,9 +244,12 @@ private Task UpdateReferencesAync(Project project, CancellationToken cancellatio
if (!_metadataPathToInfo.TryGetValue(key, out var metadataInfo) ||
metadataInfo.SymbolTreeInfo.Checksum != checksum)
{
var info = await SymbolTreeInfo.TryGetInfoForMetadataReferenceAsync(
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.
......
......@@ -23,7 +23,7 @@ public BasicEditAndContinue(VisualStudioInstanceFactory instanceFactory) : base(
protected override string LanguageName => LanguageNames.VisualBasic;
[Fact]
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/19441")]
public void UpdateActiveStatementLeafNode()
{
VisualStudio.Editor.SetText(@"
......@@ -82,7 +82,7 @@ End Sub
VisualStudio.Editor.Verify.CurrentLineText("End Try");
}
[Fact]
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/19441")]
public void EditLambdaExpression()
{
VisualStudio.Editor.SetText(@"
......@@ -113,7 +113,7 @@ End Sub
VisualStudio.ErrorList.Verify.NoBuildErrors();
}
[Fact]
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/19441")]
public void EnCWhileDebuggingFromImmediateWindow()
{
VisualStudio.Editor.SetText(@"
......@@ -177,7 +177,7 @@ End Module
VisualStudio.Workspace.WaitForAsyncOperations(FeatureAttribute.Workspace);
}
[Fact]
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/19441")]
public void MultiProjectDebuggingWhereNotAllModulesAreLoaded()
{
SetupMultiProjectSolution();
......@@ -188,7 +188,7 @@ public void MultiProjectDebuggingWhereNotAllModulesAreLoaded()
VisualStudio.ErrorList.Verify.NoErrors();
}
[Fact]
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/19441")]
public void DocumentStateTrackingReadonlyInRunMode()
{
SetupMultiProjectSolution();
......@@ -238,7 +238,7 @@ End Module
VisualStudio.Editor.Verify.IsProjectItemDirty(expectedValue: false);
}
[Fact]
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/19441")]
public void LocalsWindowUpdatesAfterLocalGetsItsTypeUpdatedDuringEnC()
{
VisualStudio.Editor.SetText(@"
......@@ -261,7 +261,7 @@ End Module
VisualStudio.LocalsWindow.Verify.CheckEntry("foo", "Single", "10");
}
[Fact]
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/19441")]
public void LocalsWindowUpdatesCorrectlyDuringEnC()
{
VisualStudio.Editor.SetText(@"
......
......@@ -74,14 +74,12 @@ internal static partial class DeclarationFinder
{
if (referenceOpt != null)
{
var info = await SymbolTreeInfo.TryGetInfoForMetadataReferenceAsync(
var info = await SymbolTreeInfo.GetInfoForMetadataReferenceAsync(
project.Solution, referenceOpt, loadOnly: false, cancellationToken: cancellationToken).ConfigureAwait(false);
if (info != null)
{
var symbols = await info.FindAsync(
var symbols = await info.FindAsync(
query, assembly, project.Id, filter, cancellationToken).ConfigureAwait(false);
list.AddRange(symbols);
}
list.AddRange(symbols);
}
}
}
......
......@@ -469,12 +469,8 @@ internal static class DependentTypeFinder
// 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.TryGetInfoForMetadataReferenceAsync(
var symbolTreeInfo = await SymbolTreeInfo.GetInfoForMetadataReferenceAsync(
project.Solution, reference, loadOnly: false, cancellationToken: cancellationToken).ConfigureAwait(false);
if (symbolTreeInfo == null)
{
return;
}
// For each type we care about, see if we can find any derived types
// in this index.
......
......@@ -19,6 +19,8 @@ internal partial class SymbolTreeInfo
[DebuggerDisplay("{GetDebuggerDisplay(),nq}")]
private struct BuilderNode
{
public static readonly BuilderNode RootNode = new BuilderNode("", RootNodeParentIndex);
public readonly string Name;
public readonly int ParentIndex;
......
......@@ -16,9 +16,9 @@
namespace Microsoft.CodeAnalysis.FindSymbols
{
internal partial class SymbolTreeInfo
internal partial class SymbolTreeInfo : IChecksummedObject
{
public readonly Checksum Checksum;
public Checksum Checksum { get; }
/// <summary>
/// To prevent lots of allocations, we concatenate all the names in all our
......@@ -105,7 +105,7 @@ internal partial class SymbolTreeInfo
private SymbolTreeInfo(
Checksum checksum,
string concatenatedNames,
Node[] sortedNodes,
Node[] sortedNodes,
Task<SpellChecker> spellCheckerTask)
{
Checksum = checksum;
......@@ -114,6 +114,15 @@ internal partial class SymbolTreeInfo
_spellCheckerTask = spellCheckerTask;
}
public static SymbolTreeInfo CreateEmpty(Checksum checksum)
{
var unsortedNodes = ImmutableArray.Create(BuilderNode.RootNode);
SortNodes(unsortedNodes, out var concatenatedNames, out var sortedNodes);
return new SymbolTreeInfo(checksum, concatenatedNames, sortedNodes,
CreateSpellCheckerAsync(checksum, concatenatedNames, sortedNodes));
}
public Task<ImmutableArray<SymbolAndProjectId>> FindAsync(
SearchQuery query, IAssemblySymbol assembly, ProjectId assemblyProjectId, SymbolFilter filter, CancellationToken cancellationToken)
{
......@@ -209,7 +218,7 @@ internal partial class SymbolTreeInfo
Bind(node, assemblySymbol.GlobalNamespace, results, cancellationToken);
}
return results.ToImmutableAndFree(); ;
return results.ToImmutableAndFree();
}
private static StringSliceComparer GetComparer(bool ignoreCase)
......@@ -322,8 +331,8 @@ private int BinarySearch(string name)
// SymbolTreeInfo. This way the SymbolTreeInfo will be ready immediately
// for non-fuzzy searches, and soon afterwards it will be able to perform
// fuzzy searches as well.
return Task.Run(() => LoadOrCreateSpellCheckerAsync(solution, checksum, filePath,
() => CreateSpellCheckerAsync(checksum, concatenatedNames, sortedNodes)));
return Task.Run(() => LoadOrCreateSpellCheckerAsync(
solution, checksum, filePath, concatenatedNames, sortedNodes));
}
private static Task<SpellChecker> CreateSpellCheckerAsync(Checksum checksum, string concatenatedNames, Node[] sortedNodes)
......
......@@ -59,22 +59,21 @@ private static Metadata GetMetadataNoThrow(PortableExecutableReference reference
}
}
public static Task<SymbolTreeInfo> TryGetInfoForMetadataReferenceAsync(
public static Task<SymbolTreeInfo> GetInfoForMetadataReferenceAsync(
Solution solution, PortableExecutableReference reference,
bool loadOnly, CancellationToken cancellationToken)
{
var checksum = GetMetadataChecksum(solution, reference, cancellationToken);
return TryGetInfoForMetadataReferenceAsync(
return GetInfoForMetadataReferenceAsync(
solution, reference, checksum,
loadOnly, cancellationToken);
}
/// <summary>
/// Produces a <see cref="SymbolTreeInfo"/> for a given <see cref="PortableExecutableReference"/>.
/// Note: can return <code>null</code> if we weren't able to actually load the metadata for some
/// reason.
/// Note: will never return null;
/// </summary>
public static Task<SymbolTreeInfo> TryGetInfoForMetadataReferenceAsync(
public static Task<SymbolTreeInfo> GetInfoForMetadataReferenceAsync(
Solution solution,
PortableExecutableReference reference,
Checksum checksum,
......@@ -84,7 +83,7 @@ private static Metadata GetMetadataNoThrow(PortableExecutableReference reference
var metadataId = GetMetadataIdNoThrow(reference);
if (metadataId == null)
{
return SpecializedTasks.Default<SymbolTreeInfo>();
return Task.FromResult(CreateEmpty(checksum));
}
// Try to acquire the data outside the lock. That way we can avoid any sort of
......@@ -99,14 +98,14 @@ private static Metadata GetMetadataNoThrow(PortableExecutableReference reference
var metadata = GetMetadataNoThrow(reference);
if (metadata == null)
{
return SpecializedTasks.Default<SymbolTreeInfo>();
return Task.FromResult(CreateEmpty(checksum));
}
return TryGetInfoForMetadataReferenceSlowAsync(
return GetInfoForMetadataReferenceSlowAsync(
solution, reference, checksum, loadOnly, metadata, cancellationToken);
}
private static async Task<SymbolTreeInfo> TryGetInfoForMetadataReferenceSlowAsync(
private static async Task<SymbolTreeInfo> GetInfoForMetadataReferenceSlowAsync(
Solution solution, PortableExecutableReference reference, Checksum checksum,
bool loadOnly, Metadata metadata, CancellationToken cancellationToken)
{
......@@ -121,11 +120,11 @@ private static Metadata GetMetadataNoThrow(PortableExecutableReference reference
return await infoTask.ConfigureAwait(false);
}
var info = await LoadOrCreateMetadataSymbolTreeInfoAsync(
var info = await TryLoadOrCreateMetadataSymbolTreeInfoAsync(
solution, reference, checksum, loadOnly, cancellationToken).ConfigureAwait(false);
if (info == null && loadOnly)
{
return null;
return CreateEmpty(checksum);
}
// Cache the result in our dictionary. Store it as a completed task so that
......@@ -147,7 +146,7 @@ private static Metadata GetMetadataNoThrow(PortableExecutableReference reference
return checksum;
}
private static Task<SymbolTreeInfo> LoadOrCreateMetadataSymbolTreeInfoAsync(
private static Task<SymbolTreeInfo> TryLoadOrCreateMetadataSymbolTreeInfoAsync(
Solution solution,
PortableExecutableReference reference,
Checksum checksum,
......@@ -156,16 +155,16 @@ private static Metadata GetMetadataNoThrow(PortableExecutableReference reference
{
var filePath = reference.FilePath;
return LoadOrCreateAsync(
var result = TryLoadOrCreateAsync(
solution,
checksum,
filePath,
loadOnly,
createAsync: () => CreateMetadataSymbolTreeInfoAsync(solution, checksum, reference, cancellationToken),
keySuffix: "_Metadata",
getPersistedChecksum: info => info.Checksum,
readObject: reader => ReadSymbolTreeInfo(reader, (names, nodes) => GetSpellCheckerTask(solution, checksum, filePath, names, nodes)),
keySuffix: "_Metadata_" + filePath,
tryReadObject: reader => TryReadSymbolTreeInfo(reader, (names, nodes) => GetSpellCheckerTask(solution, checksum, filePath, names, nodes)),
cancellationToken: cancellationToken);
Contract.ThrowIfFalse(result != null || loadOnly == true, "Result can only be null if 'loadOnly: true' was passed.");
return result;
}
private static Task<SymbolTreeInfo> CreateMetadataSymbolTreeInfoAsync(
......@@ -263,7 +262,7 @@ internal SymbolTreeInfo Create()
}
var unsortedNodes = GenerateUnsortedNodes();
return SymbolTreeInfo.CreateSymbolTreeInfo(
return CreateSymbolTreeInfo(
_solution, _checksum, _reference.FilePath, unsortedNodes, _inheritanceMap);
}
......@@ -635,7 +634,7 @@ private void EnsureParentsAndChildren(List<string> simpleNames)
private ImmutableArray<BuilderNode> GenerateUnsortedNodes()
{
var unsortedNodes = ArrayBuilder<BuilderNode>.GetInstance();
unsortedNodes.Add(new BuilderNode(name: "", parentIndex: RootNodeParentIndex));
unsortedNodes.Add(BuilderNode.RootNode);
AddUnsortedNodes(unsortedNodes, parentNode: _rootNode, parentIndex: 0);
......
......@@ -19,25 +19,6 @@ internal partial class SymbolTreeInfo : IObjectWritable
private const string PrefixMetadataSymbolTreeInfo = "<SymbolTreeInfo>";
private const string SerializationFormat = "17";
/// <summary>
/// Loads the SymbolTreeInfo for a given assembly symbol (metadata or project). If the
/// info can't be loaded, it will be created (and persisted if possible).
/// </summary>
private static Task<SymbolTreeInfo> LoadOrCreateSourceSymbolTreeInfoAsync(
Project project, Checksum checksum, bool loadOnly, CancellationToken cancellationToken)
{
return LoadOrCreateAsync(
project.Solution,
checksum,
project.FilePath,
loadOnly,
createAsync: () => CreateSourceSymbolTreeInfoAsync(project, checksum, cancellationToken),
keySuffix: "_Source",
getPersistedChecksum: info => info.Checksum,
readObject: reader => ReadSymbolTreeInfo(reader, (names, nodes) => GetSpellCheckerTask(project.Solution, checksum, project.FilePath, names, nodes)),
cancellationToken: cancellationToken);
}
/// <summary>
/// Loads the SpellChecker for a given assembly symbol (metadata or project). If the
/// info can't be loaded, it will be created (and persisted if possible).
......@@ -46,34 +27,33 @@ internal partial class SymbolTreeInfo : IObjectWritable
Solution solution,
Checksum checksum,
string filePath,
Func<Task<SpellChecker>> createAsync)
string concatenatedNames,
Node[] sortedNodes)
{
return LoadOrCreateAsync(
var result = TryLoadOrCreateAsync(
solution,
checksum,
filePath,
loadOnly: false,
createAsync: createAsync,
keySuffix: "_SpellChecker",
getPersistedChecksum: s => s.Checksum,
readObject: SpellChecker.ReadFrom,
createAsync: () => CreateSpellCheckerAsync(checksum, concatenatedNames, sortedNodes),
keySuffix: "_SpellChecker_" + filePath,
tryReadObject: SpellChecker.TryReadFrom,
cancellationToken: CancellationToken.None);
Contract.ThrowIfNull(result, "Result should never be null as we passed 'loadOnly: false'.");
return result;
}
/// <summary>
/// Generalized function for loading/creating/persisting data. Used as the common core
/// code for serialization of SymbolTreeInfos and SpellCheckers.
/// </summary>
private static async Task<T> LoadOrCreateAsync<T>(
private static async Task<T> TryLoadOrCreateAsync<T>(
Solution solution,
Checksum checksum,
string filePath,
bool loadOnly,
Func<Task<T>> createAsync,
string keySuffix,
Func<T, Checksum> getPersistedChecksum,
Func<ObjectReader, T> readObject,
CancellationToken cancellationToken) where T : class, IObjectWritable
Func<ObjectReader, T> tryReadObject,
CancellationToken cancellationToken) where T : class, IObjectWritable, IChecksummedObject
{
if (checksum == null)
{
......@@ -87,7 +67,7 @@ internal partial class SymbolTreeInfo : IObjectWritable
using (var storage = persistentStorageService.GetStorage(solution, checkBranchId: false))
{
// Get the unique key to identify our data.
var key = PrefixMetadataSymbolTreeInfo + keySuffix + "_" + filePath;
var key = PrefixMetadataSymbolTreeInfo + keySuffix;
using (var stream = await storage.ReadStreamAsync(key, cancellationToken).ConfigureAwait(false))
using (var reader = ObjectReader.TryGetReader(stream))
{
......@@ -96,8 +76,8 @@ internal partial class SymbolTreeInfo : IObjectWritable
// We have some previously persisted data. Attempt to read it back.
// If we're able to, and the version of the persisted data matches
// our version, then we can reuse this instance.
result = readObject(reader);
if (result != null && checksum == getPersistedChecksum(result))
result = tryReadObject(reader);
if (result?.Checksum == checksum)
{
return result;
}
......@@ -116,16 +96,15 @@ internal partial class SymbolTreeInfo : IObjectWritable
// Now, try to create a new instance and write it to the persistence service.
result = await createAsync().ConfigureAwait(false);
if (result != null)
Contract.ThrowIfNull(result);
using (var stream = SerializableBytes.CreateWritableStream())
using (var writer = new ObjectWriter(stream, cancellationToken: cancellationToken))
{
using (var stream = SerializableBytes.CreateWritableStream())
using (var writer = new ObjectWriter(stream, cancellationToken: cancellationToken))
{
result.WriteTo(writer);
stream.Position = 0;
result.WriteTo(writer);
stream.Position = 0;
await storage.WriteStreamAsync(key, stream, cancellationToken).ConfigureAwait(false);
}
await storage.WriteStreamAsync(key, stream, cancellationToken).ConfigureAwait(false);
}
}
......@@ -163,12 +142,12 @@ public void WriteTo(ObjectWriter writer)
internal static SymbolTreeInfo ReadSymbolTreeInfo_ForTestingPurposesOnly(
ObjectReader reader, Checksum checksum)
{
return ReadSymbolTreeInfo(reader,
return TryReadSymbolTreeInfo(reader,
(names, nodes) => Task.FromResult(
new SpellChecker(checksum, nodes.Select(n => new StringSlice(names, n.NameSpan)))));
}
private static SymbolTreeInfo ReadSymbolTreeInfo(
private static SymbolTreeInfo TryReadSymbolTreeInfo(
ObjectReader reader,
Func<string, Node[], Task<SpellChecker>> createSpellCheckerTask)
{
......
......@@ -18,9 +18,7 @@ internal partial class SymbolTreeInfo
new SimplePool<MultiDictionary<string, ISymbol>>(() => new MultiDictionary<string, ISymbol>());
private static MultiDictionary<string, ISymbol> AllocateSymbolMap()
{
return s_symbolMapPool.Allocate();
}
=> s_symbolMapPool.Allocate();
private static void FreeSymbolMap(MultiDictionary<string, ISymbol> symbolMap)
{
......@@ -31,8 +29,16 @@ private static void FreeSymbolMap(MultiDictionary<string, ISymbol> symbolMap)
public static Task<SymbolTreeInfo> GetInfoForSourceAssemblyAsync(
Project project, Checksum checksum, CancellationToken cancellationToken)
{
return LoadOrCreateSourceSymbolTreeInfoAsync(
project, checksum, loadOnly: false, cancellationToken: cancellationToken);
var result = TryLoadOrCreateAsync(
project.Solution,
checksum,
loadOnly: false,
createAsync: () => CreateSourceSymbolTreeInfoAsync(project, checksum, cancellationToken),
keySuffix: "_Source_" + project.FilePath,
tryReadObject: reader => TryReadSymbolTreeInfo(reader, (names, nodes) => GetSpellCheckerTask(project.Solution, checksum, project.FilePath, names, nodes)),
cancellationToken: cancellationToken);
Contract.ThrowIfNull(result, "Result should never be null as we passed 'loadOnly: false'.");
return result;
}
public static async Task<Checksum> GetSourceSymbolsChecksumAsync(Project project, CancellationToken cancellationToken)
......@@ -81,7 +87,7 @@ public static async Task<Checksum> GetSourceSymbolsChecksumAsync(Project project
var assembly = compilation.Assembly;
if (assembly == null)
{
return null;
return CreateEmpty(checksum);
}
var unsortedNodes = ArrayBuilder<BuilderNode>.GetInstance();
......
......@@ -10,7 +10,7 @@
namespace Roslyn.Utilities
{
internal class SpellChecker : IObjectWritable
internal class SpellChecker : IObjectWritable, IChecksummedObject
{
private const string SerializationFormat = "3";
......@@ -49,7 +49,7 @@ void IObjectWritable.WriteTo(ObjectWriter writer)
_bkTree.WriteTo(writer);
}
internal static SpellChecker ReadFrom(ObjectReader reader)
internal static SpellChecker TryReadFrom(ObjectReader reader)
{
try
{
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册