提交 01c70371 编写于 作者: C CyrusNajmabadi

Add a version header to Object-Streams. This way we can tell, up front if the...

Add a version header to Object-Streams.  This way we can tell, up front if the persistence version has changed, and if we can read the data properly or not.
上级 2133504d
......@@ -204,8 +204,14 @@ public static SyntaxNode DeserializeFrom(Stream stream, CancellationToken cancel
throw new InvalidOperationException(CodeAnalysisResources.TheStreamCannotBeReadFrom);
}
using (var reader = new StreamObjectReader(stream, knownObjects: GetDeserializationObjectData(), binder: s_defaultBinder, cancellationToken: cancellationToken))
using (var reader = StreamObjectReader.TryGetReader(stream, knownObjects: GetDeserializationObjectData(), binder: s_defaultBinder, cancellationToken: cancellationToken))
{
if (reader == null)
{
throw new ArgumentException(CodeAnalysisResources.Stream_contains_invalid_data, nameof(stream));
}
var root = (Syntax.InternalSyntax.CSharpSyntaxNode)reader.ReadValue();
return root.CreateRed();
}
......
......@@ -12,6 +12,19 @@ namespace Microsoft.CodeAnalysis.UnitTests
{
public sealed class ObjectSerializationTests
{
[Fact]
private void TestInvalidStreamVersion()
{
var stream = new MemoryStream();
stream.WriteByte(0);
stream.WriteByte(0);
stream.Position = 0;
var reader = StreamObjectReader.TryGetReader(stream);
Assert.Null(reader);
}
private void RoundTrip(Action<ObjectWriter> writeAction, Action<ObjectReader> readAction, bool recursive)
{
var stream = new MemoryStream();
......@@ -25,7 +38,7 @@ private void RoundTrip(Action<ObjectWriter> writeAction, Action<ObjectReader> re
Assert.Equal(recursive, StreamObjectReader.IsRecursive(stream));
stream.Position = 0;
using (var reader = new StreamObjectReader(stream, binder: binder))
using (var reader = StreamObjectReader.TryGetReader(stream, binder: binder))
{
readAction(reader);
}
......@@ -50,7 +63,7 @@ private T RoundTrip<T>(T value, Action<ObjectWriter, T> writeAction, Func<Object
Assert.Equal(recursive, StreamObjectReader.IsRecursive(stream));
stream.Position = 0;
using (var reader = new StreamObjectReader(stream, binder: binder))
using (var reader = StreamObjectReader.TryGetReader(stream, binder: binder))
{
return (T)readAction(reader);
}
......@@ -978,7 +991,7 @@ public void TestObjectMapLimits()
writer.Dispose();
stream.Position = 0;
using (var reader = new StreamObjectReader(stream, binder: binder))
using (var reader = StreamObjectReader.TryGetReader(stream, binder: binder))
{
for (int pass = 0; pass < 2; pass++)
{
......
......@@ -1225,6 +1225,15 @@ internal class CodeAnalysisResources {
}
}
/// <summary>
/// Looks up a localized string similar to Stream contains invalid data.
/// </summary>
internal static string Stream_contains_invalid_data {
get {
return ResourceManager.GetString("Stream_contains_invalid_data", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Stream is too long..
/// </summary>
......
......@@ -588,4 +588,7 @@
<data name="Deserialization_reader_for_0_read_incorrect_number_of_values" xml:space="preserve">
<value>Deserialization reader for '{0}' read incorrect number of values.</value>
</data>
<data name="Stream_contains_invalid_data" xml:space="preserve">
<value>Stream contains invalid data</value>
</data>
</root>
\ No newline at end of file
......@@ -27,6 +27,14 @@ namespace Roslyn.Utilities
/// </summary>
internal sealed partial class StreamObjectReader : ObjectReader, IDisposable
{
/// <summary>
/// We start the version at something reasonably random. That way an older file, with
/// some random start-bytes, has little chance of matching our version. When incrementing
/// this version, just change VersionByte2.
/// </summary>
internal const byte VersionByte1 = 0b10101010;
internal const byte VersionByte2 = 0b00000001;
private readonly BinaryReader _reader;
private readonly ObjectBinder _binder;
private readonly bool _recursive;
......@@ -67,11 +75,11 @@ internal sealed partial class StreamObjectReader : ObjectReader, IDisposable
/// <param name="knownObjects">An optional list of objects assumed known by the corresponding <see cref="StreamObjectWriter"/>.</param>
/// <param name="binder">A binder that provides object and type decoding.</param>
/// <param name="cancellationToken"></param>
public StreamObjectReader(
private StreamObjectReader(
Stream stream,
ObjectData knownObjects = null,
ObjectBinder binder = null,
CancellationToken cancellationToken = default(CancellationToken))
ObjectData knownObjects,
ObjectBinder binder,
CancellationToken cancellationToken)
{
// String serialization assumes both reader and writer to be of the same endianness.
// It can be adjusted for BigEndian if needed.
......@@ -93,6 +101,30 @@ internal sealed partial class StreamObjectReader : ObjectReader, IDisposable
}
}
/// <summary>
/// Attempts to create a <see cref="StreamObjectReader"/> from the provided <paramref name="stream"/>.
/// If the <paramref name="stream"/> does not start with a valid header, then <code>null</code> will
/// be returned.
/// </summary>
public static StreamObjectReader TryGetReader(
Stream stream,
ObjectData knownObjects = null,
ObjectBinder binder = null,
CancellationToken cancellationToken = default(CancellationToken))
{
if (stream == null)
{
return null;
}
if (stream.ReadByte() != VersionByte1 && stream.ReadByte() != VersionByte2)
{
return null;
}
return new StreamObjectReader(stream, knownObjects, binder, cancellationToken);
}
internal static bool IsRecursive(Stream stream)
{
var recursionKind = (EncodingKind)stream.ReadByte();
......
......@@ -82,6 +82,8 @@ internal sealed partial class StreamObjectWriter : ObjectWriter, IDisposable
_recursive = recursive;
_cancellationToken = cancellationToken;
WriteVersion();
if (_recursive)
{
_writer.Write((byte)EncodingKind.Recursive);
......@@ -95,6 +97,12 @@ internal sealed partial class StreamObjectWriter : ObjectWriter, IDisposable
}
}
private void WriteVersion()
{
_writer.Write(StreamObjectReader.VersionByte1);
_writer.Write(StreamObjectReader.VersionByte2);
}
public void Dispose()
{
_referenceMap.Dispose();
......
......@@ -140,7 +140,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
Throw New InvalidOperationException(CodeAnalysisResources.TheStreamCannotBeReadFrom)
End If
Using reader = New StreamObjectReader(stream, knownObjects:=GetDeserializationObjectData(), binder:=s_defaultBinder, cancellationToken:=cancellationToken)
Using reader = StreamObjectReader.TryGetReader(stream, knownObjects:=GetDeserializationObjectData(), binder:=s_defaultBinder, cancellationToken:=cancellationToken)
If reader Is Nothing Then
Throw New ArgumentException(CodeAnalysisResources.Stream_contains_invalid_data, NameOf(stream))
End If
Return DirectCast(reader.ReadValue(), InternalSyntax.VisualBasicSyntaxNode).CreateRed(Nothing, 0)
End Using
End Function
......
// 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.Generic;
using System.Collections.Immutable;
using System.IO;
using System.Threading;
......@@ -32,33 +30,25 @@ protected override int GetCount(Data data)
protected override Data TryGetExistingData(Stream stream, Document value, CancellationToken cancellationToken)
{
var list = SharedPools.Default<List<TodoItem>>().AllocateAndClear();
try
using (var reader = StreamObjectReader.TryGetReader(stream))
{
using (var reader = new StreamObjectReader(stream))
if (reader != null)
{
var format = reader.ReadString();
if (!string.Equals(format, FormatVersion))
if (string.Equals(format, FormatVersion))
{
return null;
}
var textVersion = VersionStamp.ReadFrom(reader);
var dataVersion = VersionStamp.ReadFrom(reader);
var textVersion = VersionStamp.ReadFrom(reader);
var dataVersion = VersionStamp.ReadFrom(reader);
AppendItems(reader, value, list, cancellationToken);
var list = ArrayBuilder<TodoItem>.GetInstance();
AppendItems(reader, value, list, cancellationToken);
return new Data(textVersion, dataVersion, list.ToImmutableArray<TodoItem>());
return new Data(textVersion, dataVersion, list.ToImmutableAndFree());
}
}
}
catch (Exception)
{
return null;
}
finally
{
SharedPools.Default<List<TodoItem>>().ClearAndFree(list);
}
return null;
}
protected override void WriteTo(Stream stream, Data data, CancellationToken cancellationToken)
......@@ -104,7 +94,7 @@ public ImmutableArray<TodoItem> GetItems_TestingOnly(DocumentId documentId)
return ImmutableArray<TodoItem>.Empty;
}
private void AppendItems(ObjectReader reader, Document document, List<TodoItem> list, CancellationToken cancellationToken)
private void AppendItems(ObjectReader reader, Document document, ArrayBuilder<TodoItem> list, CancellationToken cancellationToken)
{
var count = reader.ReadInt32();
for (var i = 0; i < count; i++)
......@@ -130,4 +120,4 @@ private void AppendItems(ObjectReader reader, Document document, List<TodoItem>
}
}
}
}
}
\ No newline at end of file
......@@ -54,16 +54,22 @@ public async Task<TData> TryGetExistingDataAsync(TValue value, CancellationToken
var solution = GetSolution(value);
var persistService = solution.Workspace.Services.GetService<IPersistentStorageService>();
using (var storage = persistService.GetStorage(solution))
using (var stream = await ReadStreamAsync(storage, value, cancellationToken).ConfigureAwait(false))
try
{
if (stream == null)
using (var storage = persistService.GetStorage(solution))
using (var stream = await ReadStreamAsync(storage, value, cancellationToken).ConfigureAwait(false))
{
return default(TData);
if (stream != null)
{
return TryGetExistingData(stream, value, cancellationToken);
}
}
return TryGetExistingData(stream, value, cancellationToken);
}
catch (IOException)
{
}
return default(TData);
}
public async Task PersistAsync(TValue value, TData data, CancellationToken cancellationToken)
......
......@@ -31,27 +31,23 @@ protected override int GetCount(Data data)
protected override Data TryGetExistingData(Stream stream, Document value, CancellationToken cancellationToken)
{
try
using (var reader = StreamObjectReader.TryGetReader(stream))
{
using (var reader = new StreamObjectReader(stream))
if (reader != null)
{
var format = reader.ReadString();
if (!string.Equals(format, FormatVersion, StringComparison.InvariantCulture))
if (string.Equals(format, FormatVersion, StringComparison.InvariantCulture))
{
return null;
}
var textVersion = VersionStamp.ReadFrom(reader);
var dataVersion = VersionStamp.ReadFrom(reader);
var designerAttributeArgument = reader.ReadString();
var textVersion = VersionStamp.ReadFrom(reader);
var dataVersion = VersionStamp.ReadFrom(reader);
var designerAttributeArgument = reader.ReadString();
return new Data(textVersion, dataVersion, designerAttributeArgument);
return new Data(textVersion, dataVersion, designerAttributeArgument);
}
}
}
catch (Exception)
{
return null;
}
return null;
}
protected override void WriteTo(Stream stream, Data data, CancellationToken cancellationToken)
......
......@@ -139,7 +139,7 @@ private CompilationWithAnalyzers CreateAnalyzerDriver(CompilationWithAnalyzers a
// handling of cancellation and exception
var version = await DiagnosticIncrementalAnalyzer.GetDiagnosticVersionAsync(project, cancellationToken).ConfigureAwait(false);
using (var reader = new StreamObjectReader(stream))
using (var reader = StreamObjectReader.TryGetReader(stream))
{
return DiagnosticResultSerializer.Deserialize(reader, analyzerMap, project, version, cancellationToken);
}
......
......@@ -2,6 +2,7 @@
using System;
using System.Composition;
using System.IO;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
......@@ -121,36 +122,31 @@ private static bool TryReadFrom(Project project, string keyName, out Versions ve
return false;
}
using (var storage = service.GetStorage(project.Solution))
using (var stream = storage.ReadStreamAsync(keyName, CancellationToken.None).WaitAndGetResult(CancellationToken.None))
try
{
if (stream == null)
{
return false;
}
try
using (var storage = service.GetStorage(project.Solution))
using (var stream = storage.ReadStreamAsync(keyName, CancellationToken.None).WaitAndGetResult(CancellationToken.None))
using (var reader = StreamObjectReader.TryGetReader(stream))
{
using (var reader = new StreamObjectReader(stream))
if (reader != null)
{
var formatVersion = reader.ReadInt32();
if (formatVersion != SerializationFormat)
if (formatVersion == SerializationFormat)
{
return false;
}
var persistedProjectVersion = VersionStamp.ReadFrom(reader);
var persistedSemanticVersion = VersionStamp.ReadFrom(reader);
var persistedProjectVersion = VersionStamp.ReadFrom(reader);
var persistedSemanticVersion = VersionStamp.ReadFrom(reader);
versions = new Versions(persistedProjectVersion, persistedSemanticVersion);
return true;
versions = new Versions(persistedProjectVersion, persistedSemanticVersion);
return true;
}
}
}
catch (Exception)
{
return false;
}
}
catch (IOException)
{
}
return false;
}
public async Task RecordSemanticVersionsAsync(Project project, CancellationToken cancellationToken)
......
......@@ -44,9 +44,7 @@ public override SyntaxTree ParseSyntaxTree(string fileName, ParseOptions options
}
public override SyntaxNode DeserializeNodeFrom(Stream stream, CancellationToken cancellationToken)
{
return CSharpSyntaxNode.DeserializeFrom(stream, cancellationToken);
}
=> CSharpSyntaxNode.DeserializeFrom(stream, cancellationToken);
public override bool CanCreateRecoverableTree(SyntaxNode root)
{
......
......@@ -62,18 +62,16 @@ public async Task<StrongBox<ImmutableArray<DiagnosticData>>> DeserializeAsync(ob
using (var storage = persistService.GetStorage(solution))
using (var stream = await ReadStreamAsync(storage, key, documentOrProject, cancellationToken).ConfigureAwait(false))
using (var reader = StreamObjectReader.TryGetReader(stream))
{
if (stream == null)
if (reader == null)
{
return null;
}
using (var reader = new StreamObjectReader(stream))
{
// we return StrongBox rather than ImmutableArray due to task lib's issue with allocations
// when returning default(value type)
return ReadFrom(reader, documentOrProject, cancellationToken);
}
// we return StrongBox rather than ImmutableArray due to task lib's issue with allocations
// when returning default(value type)
return ReadFrom(reader, documentOrProject, cancellationToken);
}
}
......
......@@ -95,19 +95,17 @@ internal partial class SymbolTreeInfo
// Get the unique key to identify our data.
var key = PrefixMetadataSymbolTreeInfo + prefix + keySuffix;
using (var stream = await storage.ReadStreamAsync(key, cancellationToken).ConfigureAwait(false))
using (var reader = StreamObjectReader.TryGetReader(stream))
{
if (stream != null)
if (reader != null)
{
using (var reader = new StreamObjectReader(stream))
// 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 && VersionStamp.CanReusePersistedVersion(version, getVersion(result)))
{
// 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 && VersionStamp.CanReusePersistedVersion(version, getVersion(result)))
{
return result;
}
return result;
}
}
}
......
......@@ -47,13 +47,9 @@ private static bool TryReadVersion(ObjectReader reader, string formatVersion, ou
// attempt to load from persisted state
using (var storage = persistentStorageService.GetStorage(document.Project.Solution))
using (var stream = await storage.ReadStreamAsync(document, persistenceName, cancellationToken).ConfigureAwait(false))
using (var reader = StreamObjectReader.TryGetReader(stream))
{
if (stream == null)
{
return null;
}
using (var reader = new StreamObjectReader(stream))
if (reader != null)
{
if (TryReadVersion(reader, formatVersion, out var persistVersion) &&
document.CanReusePersistedSyntaxTreeVersion(syntaxVersion, persistVersion))
......@@ -96,14 +92,12 @@ protected static async Task<bool> PrecalculatedAsync(Document document, string p
// check whether we already have info for this document
using (var storage = persistentStorageService.GetStorage(document.Project.Solution))
using (var stream = await storage.ReadStreamAsync(document, persistenceName, cancellationToken).ConfigureAwait(false))
using (var reader = StreamObjectReader.TryGetReader(stream))
{
if (stream != null)
if (reader != null)
{
using (var reader = new StreamObjectReader(stream))
{
return TryReadVersion(reader, formatVersion, out var persistVersion) &&
document.CanReusePersistedSyntaxTreeVersion(syntaxVersion, persistVersion);
}
return TryReadVersion(reader, formatVersion, out var persistVersion) &&
document.CanReusePersistedSyntaxTreeVersion(syntaxVersion, persistVersion);
}
}
......
......@@ -55,7 +55,7 @@ private class JsonRpcAssetSource : AssetSource
{
var results = new List<ValueTuple<Checksum, object>>();
using (var reader = new StreamObjectReader(stream))
using (var reader = StreamObjectReader.TryGetReader(stream))
{
var responseSessionId = reader.ReadInt32();
Contract.ThrowIfFalse(sessionId == responseSessionId);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册