private bool ReparseCommandLineIfChanged_NoLock(string commandLine) { var checksum = Checksum.Create(commandLine); if (_commandLineChecksum == checksum) { return(false); } _commandLineChecksum = checksum; // Dispose the existing stored command-line and then persist the new one so we can // recover it later. Only bother persisting things if we have a non-empty string. _commandLineStorage?.Dispose(); _commandLineStorage = null; if (commandLine.Length > 0) { _commandLineStorage = _temporaryStorageService.CreateTemporaryStreamStorage(); _commandLineStorage.WriteString(commandLine); } ReparseCommandLine_NoLock(commandLine); return(true); }
public async Task TestAssetSynchronization() { var code = @"class Test { void Method() { } }"; using var workspace = TestWorkspace.CreateCSharp(code); var solution = workspace.CurrentSolution; // build checksum await solution.State.GetChecksumAsync(CancellationToken.None); var map = await solution.GetAssetMapAsync(CancellationToken.None); using var remoteWorkspace = CreateRemoteWorkspace(); var sessionId = Checksum.Create(ImmutableArray.CreateRange(Guid.NewGuid().ToByteArray())); var storage = new SolutionAssetCache(); var assetSource = new SimpleAssetSource(workspace.Services.GetService <ISerializerService>(), map); var service = new AssetProvider(sessionId, storage, assetSource, remoteWorkspace.Services.GetService <ISerializerService>()); await service.SynchronizeAssetsAsync(new HashSet <Checksum>(map.Keys), CancellationToken.None); foreach (var kv in map) { Assert.True(storage.TryGetAsset <object>(kv.Key, out _)); } }
public void WorkspaceAnalyzer_Serailization_Desktop_Test() { var hostServices = MefHostServices.Create( MefHostServices.DefaultAssemblies.Add(typeof(Host.TemporaryStorageServiceFactory.TemporaryStorageService).Assembly)); using var tempRoot = new TempRoot(); using var workspace = new AdhocWorkspace(hostServices); var reference = CreateShadowCopiedAnalyzerReference(tempRoot); var assetBuilder = new CustomAssetBuilder(workspace); var asset = assetBuilder.Build(reference, CancellationToken.None); // verify checksum from custom asset builder uses different checksum than regular one var service = workspace.Services.GetService <IReferenceSerializationService>(); var expectedChecksum = Checksum.Create( WellKnownSynchronizationKind.AnalyzerReference, service.CreateChecksum(reference, usePathFromAssembly: false, CancellationToken.None)); Assert.Equal(expectedChecksum, asset.Checksum); // verify usePathFromAssembly return different checksum for same reference var fromFilePath = service.CreateChecksum(reference, usePathFromAssembly: false, CancellationToken.None); var fromAssembly = service.CreateChecksum(reference, usePathFromAssembly: true, CancellationToken.None); Assert.NotEqual(fromFilePath, fromAssembly); }
private bool ReparseCommandLineIfChanged_NoLock(ImmutableArray <string> arguments) { var checksum = Checksum.Create(arguments); if (_commandLineChecksum == checksum) { return(false); } _commandLineChecksum = checksum; // Dispose the existing stored command-line and then persist the new one so we can // recover it later. Only bother persisting things if we have a non-empty string. _commandLineStorage?.Dispose(); _commandLineStorage = null; if (!arguments.IsEmpty) { _commandLineStorage = _temporaryStorageService.CreateTemporaryStreamStorage(); _commandLineStorage.WriteAllLines(arguments); } ReparseCommandLine_NoLock(arguments); return(true); }
public async Task TestCleanup() { var storage = new SolutionAssetCache( cleanupInterval: TimeSpan.FromMilliseconds(1), purgeAfter: TimeSpan.FromMilliseconds(2), gcAfter: TimeSpan.FromMilliseconds(5) ); var checksum = Checksum.Create( WellKnownSynchronizationKind.Null, ImmutableArray.CreateRange(Guid.NewGuid().ToByteArray()) ); var data = new object(); Assert.True(storage.TryAddAsset(checksum, data)); for (var i = 0; i < 10; i++) { await Task.Delay(10); if (!storage.TryGetAsset(checksum, out object _)) { // asset is deleted return; } } // it should not reach here Assert.True(false, "asset not cleaned up"); }
public Checksum CreateChecksum(AnalyzerReference reference, bool usePathFromAssembly, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); using (var stream = SerializableBytes.CreateWritableStream()) using (var writer = new ObjectWriter(stream, cancellationToken: cancellationToken)) { switch (reference) { case AnalyzerFileReference file: WriteAnalyzerFileReferenceMvid(file, writer, usePathFromAssembly, cancellationToken); break; case UnresolvedAnalyzerReference unresolved: WriteUnresolvedAnalyzerReferenceTo(unresolved, writer); break; case AnalyzerReference analyzerReference when analyzerReference.GetType().FullName == VisualStudioUnresolvedAnalyzerReference: WriteUnresolvedAnalyzerReferenceTo(analyzerReference, writer); break; case AnalyzerImageReference _: // TODO: think a way to support this or a way to deal with this kind of situation. // https://github.com/dotnet/roslyn/issues/15783 throw new NotSupportedException(nameof(AnalyzerImageReference)); default: throw ExceptionUtilities.UnexpectedValue(reference); } stream.Position = 0; return(Checksum.Create(stream)); } }
private async Task TestAssetAsync(object data) { var sessionId = 0; var checksum = Checksum.Create(WellKnownSynchronizationKind.Null, ImmutableArray.CreateRange(Guid.NewGuid().ToByteArray())); using var workspace = TestWorkspace.CreateCSharp(file: @""); using var remoteWorkspace = CreateRemoteWorkspace(); var storage = new SolutionAssetCache(); var assetSource = new SimpleAssetSource(workspace.Services.GetService <ISerializerService>(), new Dictionary <Checksum, object>() { { checksum, data } }); var provider = new AssetProvider(sessionId, storage, assetSource, remoteWorkspace.Services.GetService <ISerializerService>()); var stored = await provider.GetAssetAsync <object>(checksum, CancellationToken.None); Assert.Equal(data, stored); var stored2 = await provider.GetAssetsAsync <object>(new[] { checksum }, CancellationToken.None); Assert.Equal(1, stored2.Count); Assert.Equal(checksum, stored2[0].Item1); Assert.Equal(data, stored2[0].Item2); }
public async Task TestAssets() { var sessionId = 0; var checksum = Checksum.Create(WellKnownSynchronizationKind.Null, ImmutableArray.CreateRange(Guid.NewGuid().ToByteArray())); var data = new object(); var storage = new AssetStorage(); _ = new SimpleAssetSource(storage, new Dictionary <Checksum, object>() { { checksum, data } }); var service = new AssetService(sessionId, storage, new RemoteWorkspace().Services.GetService <ISerializerService>()); var stored = await service.GetAssetAsync <object>(checksum, CancellationToken.None); Assert.Equal(data, stored); var stored2 = await service.GetAssetsAsync <object>(new[] { checksum }, CancellationToken.None); Assert.Equal(1, stored2.Count); Assert.Equal(checksum, stored2[0].Item1); Assert.Equal(data, stored2[0].Item2); }
public void TestPinnedSolutionInfo() { var checksum = Checksum.Create( WellKnownSynchronizationKind.Null, ImmutableArray.CreateRange(Guid.NewGuid().ToByteArray()) ); VerifyJsonSerialization( new PinnedSolutionInfo( scopeId: 10, fromPrimaryBranch: false, workspaceVersion: 100, solutionChecksum: checksum ), (x, y) => { return(( x.ScopeId == y.ScopeId && x.FromPrimaryBranch == y.FromPrimaryBranch && x.WorkspaceVersion == y.WorkspaceVersion && x.SolutionChecksum == y.SolutionChecksum ) ? 0 : 1); } ); }
private static Checksum CreateChecksum(WellKnownSynchronizationKind kind, object[] children) { // given children must be either Checksum or Checksums (collection of a checksum) return(Checksum.Create( kind, children.Select(c => c as Checksum ?? ((ChecksumCollection)c).Checksum) )); }
public void TestChecksum() { var checksum = Checksum.Create( WellKnownSynchronizationKind.Null, ImmutableArray.CreateRange(Guid.NewGuid().ToByteArray()) ); VerifyJsonSerialization(checksum); }
public Checksum CreateChecksum(object value, CancellationToken cancellationToken) { var kind = value.GetWellKnownSynchronizationKind(); using ( Logger.LogBlock( FunctionId.Serializer_CreateChecksum, s_logKind, kind, cancellationToken ) ) { cancellationToken.ThrowIfCancellationRequested(); if (value is IChecksummedObject checksummedObject) { return(checksummedObject.Checksum); } switch (kind) { case WellKnownSynchronizationKind.Null: return(Checksum.Null); case WellKnownSynchronizationKind.CompilationOptions: case WellKnownSynchronizationKind.ParseOptions: case WellKnownSynchronizationKind.ProjectReference: case WellKnownSynchronizationKind.OptionSet: return(Checksum.Create(kind, value, this)); case WellKnownSynchronizationKind.MetadataReference: return(Checksum.Create( kind, CreateChecksum((MetadataReference)value, cancellationToken) )); case WellKnownSynchronizationKind.AnalyzerReference: return(Checksum.Create( kind, CreateChecksum((AnalyzerReference)value, cancellationToken) )); case WellKnownSynchronizationKind.SerializableSourceText: return(Checksum.Create(kind, ((SerializableSourceText)value).GetChecksum())); case WellKnownSynchronizationKind.SourceText: return(Checksum.Create(kind, ((SourceText)value).GetChecksum())); default: // object that is not part of solution is not supported since we don't know what inputs are required to // serialize it throw ExceptionUtilities.UnexpectedValue(kind); } } }
private static Checksum CreateChecksumFromStreamWriter(WellKnownSynchronizationKind kind, Action <ObjectWriter, CancellationToken> writer) { using (var stream = SerializableBytes.CreateWritableStream()) using (var objectWriter = new ObjectWriter(stream)) { objectWriter.WriteInt32((int)kind); writer(objectWriter, CancellationToken.None); return(Checksum.Create(stream)); } }
public Checksum CreateChecksum(AnalyzerReference reference, CancellationToken cancellationToken) { using (var stream = SerializableBytes.CreateWritableStream()) using (var writer = new ObjectWriter(stream, cancellationToken: cancellationToken)) { WriteTo(reference, writer, cancellationToken); stream.Position = 0; return(Checksum.Create(stream)); } }
public void TestGetAssets() { var storage = new SolutionAssetCache(); var checksum = Checksum.Create(WellKnownSynchronizationKind.Null, ImmutableArray.CreateRange(Guid.NewGuid().ToByteArray())); var data = new object(); Assert.True(storage.TryAddAsset(checksum, data)); Assert.True(storage.TryGetAsset(checksum, out object _)); }
private Checksum CreatePortableExecutableReferenceChecksum(PortableExecutableReference reference, CancellationToken cancellationToken) { using (var stream = SerializableBytes.CreateWritableStream()) using (var writer = new ObjectWriter(stream, cancellationToken: cancellationToken)) { WriteMvidsTo(reference, writer, cancellationToken); stream.Position = 0; return(Checksum.Create(stream)); } }
public static WorkspaceAnalyzerReferenceAsset Create( AnalyzerReference reference, ISerializerService serializer, IReferenceSerializationService hostSerializationService, CancellationToken cancellationToken) { var checksum = Checksum.Create( WellKnownSynchronizationKind.AnalyzerReference, hostSerializationService.CreateChecksum(reference, usePathFromAssembly, cancellationToken)); return(new WorkspaceAnalyzerReferenceAsset(reference, serializer, checksum)); }
private static Checksum CreatePortableExecutableReferenceChecksum(PortableExecutableReference reference, CancellationToken cancellationToken) { using var stream = SerializableBytes.CreateWritableStream(); using (var writer = new ObjectWriter(stream, leaveOpen: true, cancellationToken)) { WritePortableExecutableReferencePropertiesTo(reference, writer, cancellationToken); WriteMvidsTo(TryGetMetadata(reference), writer, cancellationToken); } stream.Position = 0; return(Checksum.Create(stream)); }
private static Checksum GetMetadataChecksumSlow(Solution solution, PortableExecutableReference reference, CancellationToken cancellationToken) { return(ChecksumCache.GetOrCreate(reference, _ => { var serializer = solution.Workspace.Services.GetService <ISerializerService>(); var checksum = serializer.CreateChecksum(reference, cancellationToken); // Include serialization format version in our checksum. That way if the // version ever changes, all persisted data won't match the current checksum // we expect, and we'll recompute things. return Checksum.Create(checksum, SerializationFormatChecksum); })); }
private static async Task <AssetProvider> GetAssetProviderAsync(Workspace workspace, Workspace remoteWorkspace, Solution solution, Dictionary <Checksum, object> map = null) { // make sure checksum is calculated await solution.State.GetChecksumAsync(CancellationToken.None); map ??= new Dictionary <Checksum, object>(); await solution.AppendAssetMapAsync(map, CancellationToken.None); var sessionId = Checksum.Create(ImmutableArray.CreateRange(Guid.NewGuid().ToByteArray())); var storage = new SolutionAssetCache(); var assetSource = new SimpleAssetSource(workspace.Services.GetService <ISerializerService>(), map); return(new AssetProvider(sessionId, storage, assetSource, remoteWorkspace.Services.GetService <ISerializerService>())); }
private static string SafeName(string fullPath) { var fileName = Path.GetFileName(fullPath); // we don't want to build too long a path. So only take a portion of the text we started with. // However, we want to avoid collisions, so ensure we also append a safe short piece of text // that is based on the full text. const int MaxLength = 20; var prefix = fileName.Length > MaxLength?fileName.Substring(0, MaxLength) : fileName; var suffix = Checksum.Create(fullPath); var fullName = $"{prefix}-{suffix}"; return(StripInvalidPathChars(fullName)); }
public static async Task <Checksum> GetChecksumAsync( Document document, CancellationToken cancellationToken) { // Since we build the SyntaxTreeIndex from a SyntaxTree, we need our checksum to change // any time the SyntaxTree could have changed. Right now, that can only happen if the // text of the document changes, or the ParseOptions change. So we get the checksums // for both of those, and merge them together to make the final checksum. var projectChecksumState = await document.Project.State.GetStateChecksumsAsync(cancellationToken).ConfigureAwait(false); var parseOptionsChecksum = projectChecksumState.ParseOptions; var documentChecksumState = await document.State.GetStateChecksumsAsync(cancellationToken).ConfigureAwait(false); var textChecksum = documentChecksumState.Text; return(Checksum.Create(WellKnownSynchronizationKind.SyntaxTreeIndex, new[] { textChecksum, parseOptionsChecksum })); }
private static async Task <Checksum> ComputeSourceSymbolsChecksumAsync(ProjectState projectState, CancellationToken cancellationToken) { // The SymbolTree for source is built from the source-symbols from the project's compilation's // assembly. Specifically, we only get the name, kind and parent/child relationship of all the // child symbols. So we want to be able to reuse the index as long as none of these have // changed. The only thing that can make those source-symbols change in that manner are if // the text of any document changes, or if options for the project change. So we build our // checksum out of that data. var serializer = projectState.LanguageServices.WorkspaceServices.GetService <ISerializerService>(); var projectStateChecksums = await projectState.GetStateChecksumsAsync(cancellationToken).ConfigureAwait(false); // Order the documents by FilePath. Default ordering in the RemoteWorkspace is // to be ordered by Guid (which is not consistent across VS sessions). var textChecksumsTasks = projectState.DocumentStates.OrderBy(d => d.Value.FilePath, StringComparer.Ordinal).Select(async d => { var documentStateChecksum = await d.Value.GetStateChecksumsAsync(cancellationToken).ConfigureAwait(false); return(documentStateChecksum.Text); }); var compilationOptionsChecksum = projectStateChecksums.CompilationOptions; var parseOptionsChecksum = projectStateChecksums.ParseOptions; var textChecksums = await Task.WhenAll(textChecksumsTasks).ConfigureAwait(false); var allChecksums = ArrayBuilder <Checksum> .GetInstance(); try { allChecksums.AddRange(textChecksums); allChecksums.Add(compilationOptionsChecksum); allChecksums.Add(parseOptionsChecksum); // Include serialization format version in our checksum. That way if the // version ever changes, all persisted data won't match the current checksum // we expect, and we'll recompute things. allChecksums.Add(SerializationFormatChecksum); var checksum = Checksum.Create(WellKnownSynchronizationKind.SymbolTreeInfo, allChecksums); return(checksum); } finally { allChecksums.Free(); } }
public static async Task <Checksum> GetChecksumAsync( Document document, CancellationToken cancellationToken) { // Since we build the SyntaxTreeIndex from a SyntaxTree, we need our checksum to change // any time the SyntaxTree could have changed. Right now, that can only happen if the // text of the document changes, or the ParseOptions change. So we get the checksums // for both of those, and merge them together to make the final checksum. var documentChecksumState = await document.State.GetStateChecksumsAsync(cancellationToken).ConfigureAwait(false); var textChecksum = documentChecksumState.Text; var parseOptions = document.Project.ParseOptions; var serializer = new Serializer(document.Project.Solution.Workspace); var parseOptionsChecksum = ChecksumCache.GetOrCreate( parseOptions, _ => serializer.CreateChecksum(parseOptions, cancellationToken)); return(Checksum.Create(nameof(SyntaxTreeIndex), new[] { textChecksum, parseOptionsChecksum })); }
public string?TryGetStorageLocation(SolutionKey solutionKey) { if (solutionKey.WorkspaceKind is not(WorkspaceKind.RemoteWorkspace or WorkspaceKind.RemoteTemporaryWorkspace or WorkspaceKind.Host)) { return(null); } if (string.IsNullOrWhiteSpace(solutionKey.FilePath)) { return(null); } // Ensure that each unique workspace kind for any given solution has a unique // folder to store their data in. var cacheDirectory = GetCacheDirectory(); var kind = StripInvalidPathChars(solutionKey.WorkspaceKind); var hash = StripInvalidPathChars(Checksum.Create(solutionKey.FilePath).ToString()); return(Path.Combine(cacheDirectory, kind, hash));
public string?TryGetStorageLocation(Workspace workspace, SolutionKey solutionKey) { if (!IsSupported(workspace)) { return(null); } if (string.IsNullOrWhiteSpace(solutionKey.FilePath)) { return(null); } // Ensure that each unique workspace kind for any given solution has a unique // folder to store their data in. var cacheDirectory = GetCacheDirectory(); var kind = StripInvalidPathChars(workspace.Kind ?? ""); var hash = StripInvalidPathChars(Checksum.Create(solutionKey.FilePath).ToString()); return(Path.Combine(cacheDirectory, kind, hash));
public void WorkspaceAnalyzer_Serialization_Desktop_Test() { var hostServices = MefHostServices.Create( MefHostServices.DefaultAssemblies.Add(typeof(Host.TemporaryStorageServiceFactory.TemporaryStorageService).Assembly)); using var tempRoot = new TempRoot(); using var workspace = new AdhocWorkspace(hostServices); var reference = CreateShadowCopiedAnalyzerReference(tempRoot); var serializer = workspace.Services.GetService <ISerializerService>(); var asset = WorkspaceAnalyzerReferenceAsset.Create(reference, serializer, CancellationToken.None); // verify checksum from custom asset builder uses different checksum than regular one var expectedChecksum = Checksum.Create( WellKnownSynchronizationKind.AnalyzerReference, serializer.CreateChecksum(reference, CancellationToken.None)); Assert.Equal(expectedChecksum, asset.Checksum); }
public async Task TestAssets() { var sessionId = 0; var checksum = Checksum.Create(WellKnownSynchronizationKind.Null, ImmutableArray.CreateRange(Guid.NewGuid().ToByteArray())); var data = new object(); var storage = new AssetStorage(); var source = new TestAssetSource(storage, checksum, data); var service = new AssetService(sessionId, storage); var stored = await service.GetAssetAsync <object>(checksum, CancellationToken.None); Assert.Equal(data, stored); var stored2 = await service.GetAssetsAsync <object>(new[] { checksum }, CancellationToken.None); Assert.Equal(1, stored2.Count); Assert.Equal(checksum, stored2[0].Item1); Assert.Equal(data, stored2[0].Item2); }
public static async Task <Checksum> GetSourceSymbolsChecksumAsync(Project project, CancellationToken cancellationToken) { // The SymbolTree for source is built from the source-symbols from the project's compilation's // assembly. Specifically, we only get the name, kind and parent/child relationship of all the // child symbols. So we want to be able to reuse the index as long as none of these have // changed. The only thing that can make those source-symbols change in that manner are if // the text of any document changes, or if options for the project change. So we build our // checksum out of that data. var serializer = new Serializer(project.Solution.Workspace); var projectStateChecksums = await project.State.GetStateChecksumsAsync(cancellationToken).ConfigureAwait(false); // Order the documents by FilePath. Default ordering in the RemoteWorkspace is // to be ordered by Guid (which is not consistent across VS sessions). var textChecksumsTasks = project.Documents.OrderBy(d => d.FilePath).Select(async d => { var documentStateChecksum = await d.State.GetStateChecksumsAsync(cancellationToken).ConfigureAwait(false); return(documentStateChecksum.Text); }); var compilationOptionsChecksum = projectStateChecksums.CompilationOptions; var parseOptionsChecksum = projectStateChecksums.ParseOptions; var textChecksums = await Task.WhenAll(textChecksumsTasks).ConfigureAwait(false); var allChecksums = ArrayBuilder <Checksum> .GetInstance(); try { allChecksums.AddRange(textChecksums); allChecksums.Add(compilationOptionsChecksum); allChecksums.Add(parseOptionsChecksum); var checksum = Checksum.Create(WellKnownSynchronizationKind.SymbolTreeInfo, allChecksums); return(checksum); } finally { allChecksums.Free(); } }
public static Checksum CreateChecksum(AnalyzerReference reference, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); using var stream = SerializableBytes.CreateWritableStream(); using (var writer = new ObjectWriter(stream, leaveOpen: true, cancellationToken)) { switch (reference) { case AnalyzerFileReference file: writer.WriteString(file.FullPath); writer.WriteBoolean(IsAnalyzerReferenceWithShadowCopyLoader(file)); break; default: throw ExceptionUtilities.UnexpectedValue(reference); } } stream.Position = 0; return(Checksum.Create(stream)); }