提交 1c43a909 编写于 作者: C CyrusNajmabadi

Move to a content-based indexing, instead of version based indexing.

上级 b1d8a8c8
......@@ -27,12 +27,10 @@ public bool Serializable(Solution solution, string assemblyFilePath)
return true;
}
public bool TryGetSerializationPrefixAndVersion(Solution solution, string assemblyFilePath, out string prefix, out VersionStamp version)
public bool TryGetSerializationPrefix(Solution solution, string assemblyFilePath, out string prefix)
{
prefix = PathUtilities.GetRelativePath(solution.FilePath, assemblyFilePath);
version = VersionStamp.Create(File.GetLastWriteTimeUtc(assemblyFilePath));
return true;
}
}
}
}
\ No newline at end of file
......@@ -18,7 +18,7 @@ namespace Microsoft.CodeAnalysis.FindSymbols
{
internal partial class SymbolTreeInfo
{
private readonly VersionStamp _version;
private readonly Checksum _checksum;
/// <summary>
/// To prevent lots of allocations, we concatenate all the names in all our
......@@ -80,35 +80,35 @@ internal partial class SymbolTreeInfo
};
private SymbolTreeInfo(
VersionStamp version,
Checksum checksum,
string concatenatedNames,
Node[] sortedNodes,
Task<SpellChecker> spellCheckerTask,
OrderPreservingMultiDictionary<string, string> inheritanceMap)
: this(version, concatenatedNames, sortedNodes, spellCheckerTask)
: this(checksum, concatenatedNames, sortedNodes, spellCheckerTask)
{
var indexBasedInheritanceMap = CreateIndexBasedInheritanceMap(inheritanceMap);
_inheritanceMap = indexBasedInheritanceMap;
}
private SymbolTreeInfo(
VersionStamp version,
Checksum checksum,
string concatenatedNames,
Node[] sortedNodes,
Task<SpellChecker> spellCheckerTask,
OrderPreservingMultiDictionary<int, int> inheritanceMap)
: this(version, concatenatedNames, sortedNodes, spellCheckerTask)
: this(checksum, concatenatedNames, sortedNodes, spellCheckerTask)
{
_inheritanceMap = inheritanceMap;
}
private SymbolTreeInfo(
VersionStamp version,
Checksum checksum,
string concatenatedNames,
Node[] sortedNodes,
Task<SpellChecker> spellCheckerTask)
{
_version = version;
_checksum = checksum;
_concatenatedNames = concatenatedNames;
_nodes = ImmutableArray.Create(sortedNodes);
_spellCheckerTask = spellCheckerTask;
......@@ -315,15 +315,15 @@ private int BinarySearch(string name)
_ => new SemaphoreSlim(1);
private static Task<SpellChecker> GetSpellCheckerTask(
Solution solution, VersionStamp version, string filePath,
Solution solution, Checksum checksum, string filePath,
string concatenatedNames, Node[] sortedNodes)
{
// Create a new task to attempt to load or create the spell checker for this
// 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, filePath,
v => new SpellChecker(v, sortedNodes.Select(n => new StringSlice(concatenatedNames, n.NameSpan)))));
return Task.Run(() => LoadOrCreateSpellCheckerAsync(solution, checksum, filePath,
() => new SpellChecker(checksum, sortedNodes.Select(n => new StringSlice(concatenatedNames, n.NameSpan)))));
}
private static void SortNodes(
......@@ -472,7 +472,7 @@ private string GetName(Node node)
internal void AssertEquivalentTo(SymbolTreeInfo other)
{
Debug.Assert(_version.Equals(other._version));
Debug.Assert(_checksum.Equals(other._checksum));
Debug.Assert(_concatenatedNames == other._concatenatedNames);
Debug.Assert(_nodes.Length == other._nodes.Length);
......@@ -499,16 +499,16 @@ internal void AssertEquivalentTo(SymbolTreeInfo other)
}
private static SymbolTreeInfo CreateSymbolTreeInfo(
Solution solution, VersionStamp version,
Solution solution, Checksum checksum,
string filePath, ImmutableArray<BuilderNode> unsortedNodes,
OrderPreservingMultiDictionary<string, string> inheritanceMap)
{
SortNodes(unsortedNodes, out var concatenatedNames, out var sortedNodes);
var createSpellCheckerTask = GetSpellCheckerTask(
solution, version, filePath, concatenatedNames, sortedNodes);
solution, checksum, filePath, concatenatedNames, sortedNodes);
return new SymbolTreeInfo(
version, concatenatedNames, sortedNodes, createSpellCheckerTask, inheritanceMap);
checksum, concatenatedNames, sortedNodes, createSpellCheckerTask, inheritanceMap);
}
private OrderPreservingMultiDictionary<int, int> CreateIndexBasedInheritanceMap(
......
......@@ -10,6 +10,7 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Collections;
using Microsoft.CodeAnalysis.Shared.Utilities;
using Microsoft.CodeAnalysis.Utilities;
using Roslyn.Utilities;
......@@ -131,24 +132,33 @@ private static Metadata GetMetadataNoThrow(PortableExecutableReference reference
CancellationToken cancellationToken)
{
var filePath = reference.FilePath;
var checksum = IOUtilities.PerformIO(() =>
{
using (var stream = File.OpenRead(filePath))
{
return Checksum.Create(stream);
}
}, null);
return LoadOrCreateAsync(
solution,
checksum,
filePath,
loadOnly,
create: version => CreateMetadataSymbolTreeInfo(solution, version, reference, cancellationToken),
create: () => CreateMetadataSymbolTreeInfo(solution, checksum, reference, cancellationToken),
keySuffix: "",
getVersion: info => info._version,
getPersistedChecksum: info => info._checksum,
readObject: reader => ReadSymbolTreeInfo(reader, (version, names, nodes) => GetSpellCheckerTask(solution, version, filePath, names, nodes)),
writeObject: (w, i) => i.WriteTo(w),
cancellationToken: cancellationToken);
}
private static SymbolTreeInfo CreateMetadataSymbolTreeInfo(
Solution solution, VersionStamp version,
Solution solution, Checksum checksum,
PortableExecutableReference reference,
CancellationToken cancellationToken)
{
var creator = new MetadataInfoCreator(solution, version, reference, cancellationToken);
var creator = new MetadataInfoCreator(solution, checksum, reference, cancellationToken);
return creator.Create();
}
......@@ -158,7 +168,7 @@ private struct MetadataInfoCreator : IDisposable
private static ObjectPool<List<string>> s_stringListPool = new ObjectPool<List<string>>(() => new List<string>());
private readonly Solution _solution;
private readonly VersionStamp _version;
private readonly Checksum _checksum;
private readonly PortableExecutableReference _reference;
private readonly CancellationToken _cancellationToken;
......@@ -173,10 +183,10 @@ private struct MetadataInfoCreator : IDisposable
private readonly List<MetadataDefinition> _allTypeDefinitions;
public MetadataInfoCreator(
Solution solution, VersionStamp version, PortableExecutableReference reference, CancellationToken cancellationToken)
Solution solution, Checksum checksum, PortableExecutableReference reference, CancellationToken cancellationToken)
{
_solution = solution;
_version = version;
_checksum = checksum;
_reference = reference;
_cancellationToken = cancellationToken;
_metadataReader = null;
......@@ -239,7 +249,7 @@ internal SymbolTreeInfo Create()
var unsortedNodes = GenerateUnsortedNodes();
return SymbolTreeInfo.CreateSymbolTreeInfo(
_solution, _version, _reference.FilePath, unsortedNodes, _inheritanceMap);
_solution, _checksum, _reference.FilePath, unsortedNodes, _inheritanceMap);
}
public void Dispose()
......
......@@ -2,6 +2,7 @@
using System;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
......@@ -9,6 +10,7 @@
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Internal.Log;
using Microsoft.CodeAnalysis.Serialization;
using Microsoft.CodeAnalysis.Shared.Utilities;
using Microsoft.CodeAnalysis.Text;
using Microsoft.CodeAnalysis.Utilities;
using Roslyn.Utilities;
......@@ -17,7 +19,7 @@ namespace Microsoft.CodeAnalysis.FindSymbols
{
internal partial class SymbolTreeInfo
{
private const string PrefixMetadataSymbolTreeInfo = "<MetadataSymbolTreeInfoPersistence>_";
private const string PrefixMetadataSymbolTreeInfo = "<SymbolTreeInfo>";
private const string SerializationFormat = "15";
/// <summary>
......@@ -27,18 +29,20 @@ internal partial class SymbolTreeInfo
private static Task<SymbolTreeInfo> LoadOrCreateSourceSymbolTreeInfoAsync(
Solution solution,
IAssemblySymbol assembly,
Checksum checksum,
string filePath,
bool loadOnly,
CancellationToken cancellationToken)
{
return LoadOrCreateAsync(
solution,
checksum,
filePath,
loadOnly,
create: version => CreateSourceSymbolTreeInfo(solution, version, assembly, filePath, cancellationToken),
create: () => CreateSourceSymbolTreeInfo(solution, checksum, assembly, filePath, cancellationToken),
keySuffix: "",
getVersion: info => info._version,
readObject: reader => ReadSymbolTreeInfo(reader, (version, names, nodes) => GetSpellCheckerTask(solution, version, filePath, names, nodes)),
getPersistedChecksum: info => info._checksum,
readObject: reader => ReadSymbolTreeInfo(reader, (c, names, nodes) => GetSpellCheckerTask(solution, c, filePath, names, nodes)),
writeObject: (w, i) => i.WriteTo(w),
cancellationToken: cancellationToken);
}
......@@ -49,16 +53,18 @@ internal partial class SymbolTreeInfo
/// </summary>
private static Task<SpellChecker> LoadOrCreateSpellCheckerAsync(
Solution solution,
Checksum checksum,
string filePath,
Func<VersionStamp, SpellChecker> create)
Func<SpellChecker> create)
{
return LoadOrCreateAsync(
solution,
checksum,
filePath,
loadOnly: false,
create: create,
keySuffix: "SpellChecker",
getVersion: s => s.Version,
keySuffix: "_SpellChecker",
getPersistedChecksum: s => s.Checksum,
readObject: SpellChecker.ReadFrom,
writeObject: (w, i) => i.WriteTo(w),
cancellationToken: CancellationToken.None);
......@@ -70,27 +76,28 @@ internal partial class SymbolTreeInfo
/// </summary>
private static async Task<T> LoadOrCreateAsync<T>(
Solution solution,
Checksum checksum,
string filePath,
bool loadOnly,
Func<VersionStamp, T> create,
Func<T> create,
string keySuffix,
Func<T, VersionStamp> getVersion,
Func<T, Checksum> getPersistedChecksum,
Func<ObjectReader, T> readObject,
Action<ObjectWriter, T> writeObject,
CancellationToken cancellationToken) where T : class
{
// See if we can even use serialization. If not, we'll just have to make the value
// from scratch.
if (ShouldCreateFromScratch(solution, filePath, out var prefix, out var version, cancellationToken))
if (checksum == null || ShouldCreateFromScratch(solution, filePath, out var prefix, cancellationToken))
{
return loadOnly ? null : create(VersionStamp.Default);
return loadOnly ? null : create();
}
// Ok, we can use persistence. First try to load from the persistence service.
var persistentStorageService = solution.Workspace.Services.GetService<IPersistentStorageService>();
var persistentStorageService = (IPersistentStorageService2)solution.Workspace.Services.GetService<IPersistentStorageService>();
T result;
using (var storage = persistentStorageService.GetStorage(solution))
using (var storage = persistentStorageService.GetStorage(solution, checkBranchId: false))
{
// Get the unique key to identify our data.
var key = PrefixMetadataSymbolTreeInfo + prefix + keySuffix;
......@@ -103,7 +110,7 @@ internal partial class SymbolTreeInfo
// 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 && VersionStamp.CanReusePersistedVersion(version, getVersion(result)))
if (result != null && checksum == getPersistedChecksum(result))
{
return result;
}
......@@ -121,7 +128,7 @@ internal partial class SymbolTreeInfo
}
// Now, try to create a new instance and write it to the persistence service.
result = create(version);
result = create();
if (result != null)
{
using (var stream = SerializableBytes.CreateWritableStream())
......@@ -142,11 +149,9 @@ internal partial class SymbolTreeInfo
Solution solution,
string filePath,
out string prefix,
out VersionStamp version,
CancellationToken cancellationToken)
{
prefix = null;
version = default(VersionStamp);
var service = solution.Workspace.Services.GetService<IAssemblySerializationInfoService>();
if (service == null)
......@@ -160,7 +165,7 @@ internal partial class SymbolTreeInfo
return true;
}
if (!service.TryGetSerializationPrefixAndVersion(solution, filePath, out prefix, out version))
if (!service.TryGetSerializationPrefix(solution, filePath, out prefix))
{
return true;
}
......@@ -171,7 +176,7 @@ internal partial class SymbolTreeInfo
public void WriteTo(ObjectWriter writer)
{
writer.WriteString(SerializationFormat);
_version.WriteTo(writer);
_checksum.WriteTo(writer);
writer.WriteString(_concatenatedNames);
......@@ -199,20 +204,20 @@ public void WriteTo(ObjectWriter writer)
internal static SymbolTreeInfo ReadSymbolTreeInfo_ForTestingPurposesOnly(ObjectReader reader)
{
return ReadSymbolTreeInfo(reader,
(version, names, nodes) => Task.FromResult(
new SpellChecker(version, nodes.Select(n => new StringSlice(names, n.NameSpan)))));
(checksum, names, nodes) => Task.FromResult(
new SpellChecker(checksum, nodes.Select(n => new StringSlice(names, n.NameSpan)))));
}
private static SymbolTreeInfo ReadSymbolTreeInfo(
ObjectReader reader,
Func<VersionStamp, string, Node[], Task<SpellChecker>> createSpellCheckerTask)
Func<Checksum, string, Node[], Task<SpellChecker>> createSpellCheckerTask)
{
try
{
var formatVersion = reader.ReadString();
if (string.Equals(formatVersion, SerializationFormat, StringComparison.Ordinal))
{
var version = VersionStamp.ReadFrom(reader);
var checksum = Checksum.ReadFrom(reader);
var concatenatedNames = reader.ReadString();
......@@ -241,8 +246,8 @@ internal static SymbolTreeInfo ReadSymbolTreeInfo_ForTestingPurposesOnly(ObjectR
}
}
var spellCheckerTask = createSpellCheckerTask(version, concatenatedNames, nodes);
return new SymbolTreeInfo(version, concatenatedNames, nodes, spellCheckerTask, inheritanceMap);
var spellCheckerTask = createSpellCheckerTask(checksum, concatenatedNames, nodes);
return new SymbolTreeInfo(checksum, concatenatedNames, nodes, spellCheckerTask, inheritanceMap);
}
}
catch
......
......@@ -28,14 +28,15 @@ private static void FreeSymbolMap(MultiDictionary<string, ISymbol> symbolMap)
Project project, CancellationToken cancellationToken)
{
var compilation = await project.GetCompilationAsync(cancellationToken).ConfigureAwait(false);
var checksum = await project.State.GetChecksumAsync(cancellationToken).ConfigureAwait(false);
return await LoadOrCreateSourceSymbolTreeInfoAsync(
project.Solution, compilation.Assembly, project.FilePath,
project.Solution, compilation.Assembly, checksum, project.FilePath,
loadOnly: false, cancellationToken: cancellationToken).ConfigureAwait(false);
}
internal static SymbolTreeInfo CreateSourceSymbolTreeInfo(
Solution solution, VersionStamp version, IAssemblySymbol assembly,
Solution solution, Checksum checksum, IAssemblySymbol assembly,
string filePath, CancellationToken cancellationToken)
{
if (assembly == null)
......@@ -49,7 +50,7 @@ private static void FreeSymbolMap(MultiDictionary<string, ISymbol> symbolMap)
GenerateSourceNodes(assembly.GlobalNamespace, unsortedNodes, s_getMembersNoPrivate);
return CreateSymbolTreeInfo(
solution, version, filePath, unsortedNodes.ToImmutableAndFree(),
solution, checksum, filePath, unsortedNodes.ToImmutableAndFree(),
inheritanceMap: new OrderPreservingMultiDictionary<string, string>());
}
......
......@@ -14,11 +14,9 @@ public bool Serializable(Solution solution, string assemblyFilePath)
return false;
}
public bool TryGetSerializationPrefixAndVersion(Solution solution, string assemblyFilePath, out string prefix, out VersionStamp version)
public bool TryGetSerializationPrefix(Solution solution, string assemblyFilePath, out string prefix)
{
prefix = string.Empty;
version = VersionStamp.Default;
return false;
}
}
......
......@@ -7,6 +7,6 @@ namespace Microsoft.CodeAnalysis.Serialization
internal interface IAssemblySerializationInfoService : IWorkspaceService
{
bool Serializable(Solution solution, string assemblyFilePath);
bool TryGetSerializationPrefixAndVersion(Solution solution, string assemblyFilePath, out string prefix, out VersionStamp version);
bool TryGetSerializationPrefix(Solution solution, string assemblyFilePath, out string prefix);
}
}
......@@ -12,19 +12,19 @@ namespace Roslyn.Utilities
{
internal class SpellChecker
{
private const string SerializationFormat = "2";
private const string SerializationFormat = "3";
public VersionStamp Version { get; }
public Checksum Checksum { get; }
private readonly BKTree _bkTree;
public SpellChecker(VersionStamp version, BKTree bKTree)
public SpellChecker(Checksum checksum, BKTree bKTree)
{
Version = version;
Checksum = checksum;
_bkTree = bKTree;
}
public SpellChecker(VersionStamp version, IEnumerable<StringSlice> corpus)
: this(version, BKTree.Create(corpus))
public SpellChecker(Checksum checksum, IEnumerable<StringSlice> corpus)
: this(checksum, BKTree.Create(corpus))
{
}
......@@ -45,7 +45,7 @@ public IList<string> FindSimilarWords(string value, bool substringsAreSimilar)
internal void WriteTo(ObjectWriter writer)
{
writer.WriteString(SerializationFormat);
Version.WriteTo(writer);
Checksum.WriteTo(writer);
_bkTree.WriteTo(writer);
}
......@@ -56,11 +56,11 @@ internal static SpellChecker ReadFrom(ObjectReader reader)
var formatVersion = reader.ReadString();
if (string.Equals(formatVersion, SerializationFormat, StringComparison.Ordinal))
{
var version = VersionStamp.ReadFrom(reader);
var checksum = Checksum.ReadFrom(reader);
var bkTree = BKTree.ReadFrom(reader);
if (bkTree != null)
{
return new SpellChecker(version, bkTree);
return new SpellChecker(checksum, bkTree);
}
}
}
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using System.Threading;
......@@ -541,9 +542,8 @@ public async Task TestSymbolTreeInfoSerialization()
////var assembly = compilation.Assembly;
// create symbol tree info from assembly
var version = VersionStamp.Create();
var info = SymbolTreeInfo.CreateSourceSymbolTreeInfo(
solution, version, assembly, "", cancellationToken: CancellationToken.None);
solution, Checksum.Null, assembly, "", cancellationToken: CancellationToken.None);
using (var writerStream = new MemoryStream())
{
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册