提交 e9f4b033 编写于 作者: T Tomas Matousek

Replace PdbOutputInfo with EmitStreamProvider

......@@ -40,7 +40,6 @@ internal static class EmitHelpers
throw;
}
var pdbName = FileNameUtilities.ChangeExtension(compilation.SourceModule.Name, "pdb");
var diagnostics = DiagnosticBag.GetInstance();
var emitOptions = EmitOptions.Default;
......@@ -64,11 +63,11 @@ internal static class EmitHelpers
testData.Module = moduleBeingBuilt;
}
baseline = moduleBeingBuilt.PreviousGeneration;
var definitionMap = moduleBeingBuilt.PreviousDefinitions;
var changes = moduleBeingBuilt.Changes;
EmitBaseline newBaseline = null;
if (compilation.Compile(
moduleBeingBuilt,
win32Resources: null,
......@@ -81,48 +80,26 @@ internal static class EmitHelpers
// Map the definitions from the previous compilation to the current compilation.
// This must be done after compiling above since synthesized definitions
// (generated when compiling method bodies) may be required.
baseline = MapToCompilation(compilation, moduleBeingBuilt);
var pdbOutputInfo = new Cci.PdbOutputInfo(pdbName, pdbStream);
using (var pdbWriter = new Cci.PdbWriter(pdbOutputInfo, (testData != null) ? testData.SymWriterFactory : null))
{
var context = new EmitContext(moduleBeingBuilt, null, diagnostics);
var encId = Guid.NewGuid();
try
{
var writer = new DeltaMetadataWriter(
context,
compilation.MessageProvider,
baseline,
encId,
definitionMap,
changes,
cancellationToken);
Cci.MetadataSizes metadataSizes;
writer.WriteMetadataAndIL(pdbWriter, metadataStream, ilStream, out metadataSizes);
writer.GetMethodTokens(updatedMethods);
bool hasErrors = diagnostics.HasAnyErrors();
return new EmitDifferenceResult(
success: !hasErrors,
diagnostics: diagnostics.ToReadOnlyAndFree(),
baseline: hasErrors ? null : writer.GetDelta(baseline, compilation, encId, metadataSizes));
}
catch (Cci.PdbWritingException e)
{
diagnostics.Add(ErrorCode.FTL_DebugEmitFailure, Location.None, e.Message);
}
catch (PermissionSetFileReadException e)
{
diagnostics.Add(ErrorCode.ERR_PermissionSetAttributeFileReadError, Location.None, e.FileName, e.PropertyName, e.Message);
}
}
var mappedBaseline = MapToCompilation(compilation, moduleBeingBuilt);
newBaseline = compilation.SerializeToDeltaStreams(
moduleBeingBuilt,
mappedBaseline,
definitionMap,
changes,
metadataStream,
ilStream,
pdbStream,
updatedMethods,
diagnostics,
testData?.SymWriterFactory,
cancellationToken);
}
return new EmitDifferenceResult(success: false, diagnostics: diagnostics.ToReadOnlyAndFree(), baseline: null);
return new EmitDifferenceResult(
success: newBaseline != null,
diagnostics: diagnostics.ToReadOnlyAndFree(),
baseline: newBaseline);
}
/// <summary>
......
......@@ -32,13 +32,10 @@ private Guid CompiledGuid(string source, string assemblyName)
return result;
}
private ImmutableArray<byte> GetBytesEmitted(string source, Platform platform, bool debug, bool deterministic)
private ImmutableArray<byte> EmitDeterministic(string source, Platform platform, bool debug)
{
var options = (debug ? TestOptions.DebugExe : TestOptions.ReleaseExe).WithPlatform(platform);
if (deterministic)
{
options = options.WithFeatures((new[] { "dEtErmInIstIc" }).AsImmutable()); // expect case-insensitivity
}
options = options.WithFeatures((new[] { "dEtErmInIstIc" }).AsImmutable()); // expect case-insensitivity
var compilation = CreateCompilation(source, assemblyName: "DeterminismTest", references: new[] { MscorlibRef }, options: options);
......@@ -49,19 +46,6 @@ private ImmutableArray<byte> GetBytesEmitted(string source, Platform platform, b
return compilation.EmitToArray();
}
private class ImmutableByteArrayEqualityComparer : IEqualityComparer<ImmutableArray<byte>>
{
public bool Equals(ImmutableArray<byte> x, ImmutableArray<byte> y)
{
return x.SequenceEqual(y);
}
public int GetHashCode(ImmutableArray<byte> obj)
{
return obj.GetHashCode();
}
}
[Fact(Skip = "900646"), WorkItem(900646)]
public void Simple()
{
......@@ -80,28 +64,37 @@ public void Simple()
}
[Fact]
public void CompareAllBytesEmitted()
public void CompareAllBytesEmitted_Release()
{
var source =
@"class Program
{
public static void Main(string[] args) {}
}";
var comparer = new ImmutableByteArrayEqualityComparer();
var result1 = EmitDeterministic(source, platform: Platform.AnyCpu32BitPreferred, debug: false);
var result2 = EmitDeterministic(source, platform: Platform.AnyCpu32BitPreferred, debug: false);
AssertEx.Equal(result1, result2);
var result1 = GetBytesEmitted(source, platform: Platform.AnyCpu32BitPreferred, debug: true, deterministic: true);
var result2 = GetBytesEmitted(source, platform: Platform.AnyCpu32BitPreferred, debug: true, deterministic: true);
Assert.Equal(result1, result2, comparer);
var result3 = EmitDeterministic(source, platform: Platform.X64, debug: false);
var result4 = EmitDeterministic(source, platform: Platform.X64, debug: false);
AssertEx.Equal(result3, result4);
}
var result3 = GetBytesEmitted(source, platform: Platform.X64, debug: false, deterministic: true);
var result4 = GetBytesEmitted(source, platform: Platform.X64, debug: false, deterministic: true);
Assert.Equal(result3, result4, comparer);
Assert.NotEqual(result1, result3, comparer);
[Fact(Skip="https://github.com/dotnet/roslyn/issues/926"), WorkItem(926)]
public void CompareAllBytesEmitted_Debug()
{
var source =
@"class Program
{
public static void Main(string[] args) {}
}";
var result1 = EmitDeterministic(source, platform: Platform.AnyCpu32BitPreferred, debug: true);
var result2 = EmitDeterministic(source, platform: Platform.AnyCpu32BitPreferred, debug: true);
AssertEx.Equal(result1, result2);
var result5 = GetBytesEmitted(source, platform: Platform.X64, debug: false, deterministic: false);
var result6 = GetBytesEmitted(source, platform: Platform.X64, debug: false, deterministic: false);
Assert.NotEqual(result5, result6, comparer);
Assert.NotEqual(result3, result5, comparer);
var result3 = EmitDeterministic(source, platform: Platform.X64, debug: true);
var result4 = EmitDeterministic(source, platform: Platform.X64, debug: true);
AssertEx.Equal(result3, result4);
}
[Fact]
......
......@@ -16,46 +16,46 @@ internal abstract partial class CommonCompiler
/// </summary>
private sealed class CompilerEmitStreamProvider : Compilation.EmitStreamProvider, IDisposable
{
private static Stream s_uninitialized = Stream.Null;
private readonly CommonCompiler _compiler;
private readonly TouchedFileLogger _touchedFileLogger;
private readonly string _filePath;
private Stream _stream;
private readonly bool _streamCreatedByNativePdbWriter;
private Stream _lazyStream;
internal CompilerEmitStreamProvider(CommonCompiler compiler, TouchedFileLogger touchedFileLogger, string filePath)
internal CompilerEmitStreamProvider(CommonCompiler compiler, string filePath, bool streamCreatedByNativePdbWriter)
{
_compiler = compiler;
_touchedFileLogger = touchedFileLogger;
_filePath = filePath;
_streamCreatedByNativePdbWriter = streamCreatedByNativePdbWriter;
_lazyStream = s_uninitialized;
}
public void Dispose()
{
_stream?.Dispose();
_stream = null;
if (_lazyStream != s_uninitialized)
{
_lazyStream?.Dispose();
_lazyStream = s_uninitialized;
}
}
public override Stream GetStream(DiagnosticBag diagnostics)
{
if (_stream == null)
if (_lazyStream == s_uninitialized)
{
_stream = OpenFile(_filePath, diagnostics);
_lazyStream = _streamCreatedByNativePdbWriter ? null : OpenFile(_filePath, diagnostics);
}
return _stream;
return _lazyStream;
}
private Stream OpenFile(string filePath, DiagnosticBag diagnostics)
{
try
{
Stream stream = _compiler.FileOpen(filePath, FileMode.Create, FileAccess.ReadWrite, FileShare.None);
if (_touchedFileLogger != null)
{
_touchedFileLogger.AddWritten(filePath);
}
return stream;
return _compiler.FileOpen(filePath, FileMode.Create, FileAccess.ReadWrite, FileShare.None);
}
catch (Exception e)
{
......
......@@ -399,27 +399,27 @@ private int RunCore(TextWriter consoleOutput, ErrorLogger errorLogger, Cancellat
string finalPdbFilePath;
string finalXmlFilePath;
FileStream xml = null;
FileStream xmlStreamOpt = null;
cancellationToken.ThrowIfCancellationRequested();
finalXmlFilePath = Arguments.DocumentationPath;
if (finalXmlFilePath != null)
{
xml = OpenFile(finalXmlFilePath, consoleOutput, FileMode.OpenOrCreate, FileAccess.Write, FileShare.ReadWrite | FileShare.Delete);
if (xml == null)
xmlStreamOpt = OpenFile(finalXmlFilePath, consoleOutput, FileMode.OpenOrCreate, FileAccess.Write, FileShare.ReadWrite | FileShare.Delete);
if (xmlStreamOpt == null)
{
return Failed;
}
xml.SetLength(0);
xmlStreamOpt.SetLength(0);
}
cancellationToken.ThrowIfCancellationRequested();
IEnumerable<DiagnosticInfo> errors;
using (var win32Res = GetWin32Resources(Arguments, compilation, out errors))
using (xml)
using (var win32ResourceStreamOpt = GetWin32Resources(Arguments, compilation, out errors))
using (xmlStreamOpt)
{
if (ReportErrors(errors, consoleOutput, errorLogger))
{
......@@ -438,25 +438,27 @@ private int RunCore(TextWriter consoleOutput, ErrorLogger errorLogger, Cancellat
WithOutputNameOverride(outputName).
WithPdbFilePath(finalPdbFilePath);
var pdbOutputInfo = Arguments.EmitPdb
? new Cci.PdbOutputInfo(finalPdbFilePath)
: Cci.PdbOutputInfo.None;
using (var peStreamProvider = new CompilerEmitStreamProvider(this, touchedFilesLogger, finalOutputPath))
using (var peStreamProvider = new CompilerEmitStreamProvider(this, finalOutputPath, streamCreatedByNativePdbWriter: false))
using (var pdbStreamProviderOpt = Arguments.EmitPdb ? new CompilerEmitStreamProvider(this, finalOutputPath, streamCreatedByNativePdbWriter: true) : null)
{
emitResult = compilation.Emit(
peStreamProvider,
pdbOutputInfo,
xml,
win32Res,
pdbStreamProviderOpt,
(xmlStreamOpt != null) ? new Compilation.SimpleEmitStreamProvider(xmlStreamOpt) : null,
(win32ResourceStreamOpt != null) ? new Compilation.SimpleEmitStreamProvider(win32ResourceStreamOpt) : null,
Arguments.ManifestResources,
emitOptions,
getAnalyzerDiagnostics,
cancellationToken);
if (emitResult.Success && !pdbOutputInfo.IsNone && touchedFilesLogger != null)
if (emitResult.Success && touchedFilesLogger != null)
{
touchedFilesLogger.AddWritten(pdbOutputInfo.FileName);
if (pdbStreamProviderOpt != null)
{
touchedFilesLogger.AddWritten(finalPdbFilePath);
}
touchedFilesLogger.AddWritten(finalOutputPath);
}
}
}
......
......@@ -35,6 +35,7 @@ internal sealed class SimpleEmitStreamProvider : EmitStreamProvider
internal SimpleEmitStreamProvider(Stream stream)
{
Debug.Assert(stream != null);
_stream = stream;
}
......
......@@ -51,6 +51,8 @@ public abstract partial class Compilation
/// </summary>
private SmallDictionary<int, bool> _lazyMakeMemberMissingMap;
private Dictionary<string, string> _lazyFeatures;
internal Compilation(
string name,
ImmutableArray<MetadataReference> references,
......@@ -1369,9 +1371,9 @@ internal void EnsureAnonymousTypeTemplates(CancellationToken cancellationToken)
/// Emit the IL for the compiled source code into the specified stream.
/// </summary>
/// <param name="peStreamProvider">Provides the PE stream the compiler will write to.</param>
/// <param name="pdbOutputInfo">Provides the PDB stream the compiler will write to.</param>
/// <param name="xmlDocumentationStream">Stream to which the compilation's XML documentation will be written. Null to forego XML generation.</param>
/// <param name="win32Resources">Stream from which the compilation's Win32 resources will be read (in RES format).
/// <param name="pdbStreamProvider">Provides the PDB stream the compiler will write to.</param>
/// <param name="xmlDocumentationStreamProvider">Stream to which the compilation's XML documentation will be written. Null to forego XML generation.</param>
/// <param name="win32ResourcesProvider">Stream from which the compilation's Win32 resources will be read (in RES format).
/// Null to indicate that there are none. The RES format begins with a null resource entry.</param>
/// <param name="manifestResources">List of the compilation's managed resources. Null to indicate that there are none.</param>
/// <param name="options">Emit options.</param>
......@@ -1379,19 +1381,19 @@ internal void EnsureAnonymousTypeTemplates(CancellationToken cancellationToken)
/// <param name="cancellationToken">To cancel the emit process.</param>
internal EmitResult Emit(
EmitStreamProvider peStreamProvider,
Cci.PdbOutputInfo pdbOutputInfo,
Stream xmlDocumentationStream = null,
Stream win32Resources = null,
IEnumerable<ResourceDescription> manifestResources = null,
EmitOptions options = null,
Func<ImmutableArray<Diagnostic>> getHostDiagnostics = null,
CancellationToken cancellationToken = default(CancellationToken))
EmitStreamProvider pdbStreamProvider,
EmitStreamProvider xmlDocumentationStreamProvider,
EmitStreamProvider win32ResourcesProvider,
IEnumerable<ResourceDescription> manifestResources,
EmitOptions options,
Func<ImmutableArray<Diagnostic>> getHostDiagnostics,
CancellationToken cancellationToken)
{
return Emit(
peStreamProvider,
pdbOutputInfo,
new SimpleEmitStreamProvider(xmlDocumentationStream),
new SimpleEmitStreamProvider(win32Resources),
pdbStreamProvider,
xmlDocumentationStreamProvider,
win32ResourcesProvider,
manifestResources,
options,
testData: null,
......@@ -1399,6 +1401,34 @@ internal void EnsureAnonymousTypeTemplates(CancellationToken cancellationToken)
cancellationToken: cancellationToken);
}
/// <summary>
/// This overload is only intended to be directly called by tests that want to pass <paramref name="testData"/>.
/// The map is used for storing a list of methods and their associated IL.
/// </summary>
/// <returns>True if emit succeeded.</returns>
internal EmitResult Emit(
Stream peStream,
Stream pdbStream,
Stream xmlDocumentationStream,
Stream win32Resources,
IEnumerable<ResourceDescription> manifestResources,
EmitOptions options,
CompilationTestData testData,
Func<ImmutableArray<Diagnostic>> getHostDiagnostics,
CancellationToken cancellationToken)
{
return Emit(
new SimpleEmitStreamProvider(peStream),
(pdbStream != null) ? new SimpleEmitStreamProvider(pdbStream) : null,
(xmlDocumentationStream != null) ? new SimpleEmitStreamProvider(xmlDocumentationStream) : null,
(win32Resources != null) ? new SimpleEmitStreamProvider(win32Resources) : null,
manifestResources,
options,
testData,
getHostDiagnostics,
cancellationToken);
}
/// <summary>
/// Emit the differences between the compilation and the previous generation
/// for Edit and Continue. The differences are expressed as added and changed
......@@ -1481,35 +1511,7 @@ internal void EnsureAnonymousTypeTemplates(CancellationToken cancellationToken)
ICollection<MethodDefinitionHandle> updatedMethodHandles,
CompilationTestData testData,
CancellationToken cancellationToken);
/// <summary>
/// This overload is only intended to be directly called by tests that want to pass <paramref name="testData"/>.
/// The map is used for storing a list of methods and their associated IL.
/// </summary>
/// <returns>True if emit succeeded.</returns>
internal EmitResult Emit(
Stream peStream,
Stream pdbStream,
Stream xmlDocumentationStream,
Stream win32Resources,
IEnumerable<ResourceDescription> manifestResources,
EmitOptions options,
CompilationTestData testData,
Func<ImmutableArray<Diagnostic>> getHostDiagnostics,
CancellationToken cancellationToken)
{
return Emit(
new SimpleEmitStreamProvider(peStream),
new Cci.PdbOutputInfo(pdbStream),
new SimpleEmitStreamProvider(xmlDocumentationStream),
new SimpleEmitStreamProvider(win32Resources),
manifestResources,
options,
testData,
getHostDiagnostics,
cancellationToken);
}
/// <summary>
/// This overload is only intended to be directly called by tests that want to pass <paramref name="testData"/>.
/// The map is used for storing a list of methods and their associated IL.
......@@ -1517,7 +1519,7 @@ internal void EnsureAnonymousTypeTemplates(CancellationToken cancellationToken)
/// <returns>True if emit succeeded.</returns>
internal EmitResult Emit(
EmitStreamProvider peStreamProvider,
Cci.PdbOutputInfo pdbOutputInfo,
EmitStreamProvider pdbStreamProvider,
EmitStreamProvider xmlDocumentationStreamProvider,
EmitStreamProvider win32ResourcesStreamProvider,
IEnumerable<ResourceDescription> manifestResources,
......@@ -1568,13 +1570,13 @@ internal void EnsureAnonymousTypeTemplates(CancellationToken cancellationToken)
return ToEmitResultAndFree(diagnostics, success: false);
}
var win32Resources = win32ResourcesStreamProvider.GetStream(diagnostics);
var xmlDocumentationStream = xmlDocumentationStreamProvider.GetStream(diagnostics);
var win32Resources = win32ResourcesStreamProvider?.GetStream(diagnostics);
var xmlDocumentationStream = xmlDocumentationStreamProvider?.GetStream(diagnostics);
if (!this.Compile(
moduleBeingBuilt,
win32Resources,
xmlDocumentationStream,
generateDebugInfo: pdbOutputInfo.IsValid,
generateDebugInfo: pdbStreamProvider != null,
diagnostics: diagnostics,
filterOpt: null,
cancellationToken: cancellationToken))
......@@ -1583,6 +1585,7 @@ internal void EnsureAnonymousTypeTemplates(CancellationToken cancellationToken)
}
var hostDiagnostics = getHostDiagnostics?.Invoke() ?? ImmutableArray<Diagnostic>.Empty;
diagnostics.AddRange(hostDiagnostics);
if (hostDiagnostics.Any(x => x.Severity == DiagnosticSeverity.Error))
{
......@@ -1592,7 +1595,7 @@ internal void EnsureAnonymousTypeTemplates(CancellationToken cancellationToken)
bool success = SerializeToPeStream(
moduleBeingBuilt,
peStreamProvider,
pdbOutputInfo,
pdbStreamProvider,
testData?.SymWriterFactory,
diagnostics,
metadataOnly: options.EmitMetadataOnly,
......@@ -1605,11 +1608,11 @@ private static EmitResult ToEmitResultAndFree(DiagnosticBag diagnostics, bool su
{
return new EmitResult(success, diagnostics.ToReadOnlyAndFree());
}
internal bool SerializeToPeStream(
CommonPEModuleBuilder moduleBeingBuilt,
EmitStreamProvider peStreamProvider,
Cci.PdbOutputInfo pdbOutputInfo,
EmitStreamProvider pdbStreamProvider,
Func<object> testSymWriterFactory,
DiagnosticBag diagnostics,
bool metadataOnly,
......@@ -1621,7 +1624,7 @@ private static EmitResult ToEmitResultAndFree(DiagnosticBag diagnostics, bool su
Stream signingInputStream = null;
DiagnosticBag metadataDiagnostics = null;
DiagnosticBag pdbBag = null;
Stream pdbOriginalStream = null;
Stream pdbStream = null;
Stream pdbTempStream = null;
Stream peStream = null;
Stream peTempStream = null;
......@@ -1630,29 +1633,36 @@ private static EmitResult ToEmitResultAndFree(DiagnosticBag diagnostics, bool su
try
{
if (pdbOutputInfo.IsValid)
if (pdbStreamProvider != null)
{
// The calls ISymUnmanagedWriter2.GetDebugInfo require a file name in order to succeed. This is
// frequently used during PDB writing. Ensure a name is provided here in the case we were given
// only a Stream value.
if (pdbOutputInfo.Stream != null && pdbOutputInfo.FileName == null)
Func<Stream> getNativePdbStream = () =>
{
pdbOutputInfo = new Cci.PdbOutputInfo(FileNameUtilities.ChangeExtension(SourceModule.Name, "pdb"), pdbOutputInfo.Stream);
}
pdbStream = pdbStreamProvider.GetStream(diagnostics);
if (pdbStream == null)
{
return null;
}
// Native PDB writer is able to update an existing stream.
// It checks for length to determine whether the given stream has existing data to be updated,
// or whether it should start writing PDB data from scratch. Thus if not writing to a seekable empty stream ,
// let's create an in-memory temp stream for the PDB writer and copy all data to the actual stream at once at the end.
if (pdbOutputInfo.Stream != null && (!pdbOutputInfo.Stream.CanSeek || pdbOutputInfo.Stream.Length != 0))
{
pdbOriginalStream = pdbOutputInfo.Stream;
pdbTempStream = new MemoryStream();
pdbOutputInfo = pdbOutputInfo.WithStream(pdbTempStream);
}
var retStream = pdbStream;
// Native PDB writer is able to update an existing stream.
// It checks for length to determine whether the given stream has existing data to be updated,
// or whether it should start writing PDB data from scratch. Thus if not writing to a seekable empty stream ,
// let's create an in-memory temp stream for the PDB writer and copy all data to the actual stream at once at the end.
if (!retStream.CanSeek || retStream.Length != 0)
{
retStream = pdbTempStream = new MemoryStream();
}
return retStream;
};
// The calls ISymUnmanagedWriter2.GetDebugInfo require a file name in order to succeed. This is
// frequently used during PDB writing. Ensure a name is provided here in the case we were given
// only a Stream value.
nativePdbWriter = new Cci.PdbWriter(
pdbOutputInfo,
getNativePdbStream,
moduleBeingBuilt.EmitOptions.PdbFilePath ?? FileNameUtilities.ChangeExtension(SourceModule.Name, "pdb"),
testSymWriterFactory);
}
......@@ -1719,7 +1729,7 @@ private static EmitResult ToEmitResultAndFree(DiagnosticBag diagnostics, bool su
nativePdbWriter.WritePdbToOutput();
pdbTempStream.Position = 0;
pdbTempStream.CopyTo(pdbOriginalStream);
pdbTempStream.CopyTo(pdbStream);
}
}
catch (Cci.PdbWritingException ex)
......@@ -1772,7 +1782,57 @@ private static EmitResult ToEmitResultAndFree(DiagnosticBag diagnostics, bool su
return true;
}
private Dictionary<string, string> _lazyFeatures;
internal EmitBaseline SerializeToDeltaStreams(
CommonPEModuleBuilder moduleBeingBuilt,
EmitBaseline baseline,
DefinitionMap definitionMap,
SymbolChanges changes,
Stream metadataStream,
Stream ilStream,
Stream pdbStream,
ICollection<MethodDefinitionHandle> updatedMethods,
DiagnosticBag diagnostics,
Func<object> testSymWriterFactory,
CancellationToken cancellationToken)
{
using (var pdbWriter = new Cci.PdbWriter(
() => pdbStream,
moduleBeingBuilt.EmitOptions.PdbFilePath ?? FileNameUtilities.ChangeExtension(SourceModule.Name, "pdb"),
testSymWriterFactory))
{
var context = new EmitContext((Cci.IModule)moduleBeingBuilt, null, diagnostics);
var encId = Guid.NewGuid();
try
{
var writer = new DeltaMetadataWriter(
context,
MessageProvider,
baseline,
encId,
definitionMap,
changes,
cancellationToken);
Cci.MetadataSizes metadataSizes;
writer.WriteMetadataAndIL(pdbWriter, metadataStream, ilStream, out metadataSizes);
writer.GetMethodTokens(updatedMethods);
return diagnostics.HasAnyErrors() ? null : writer.GetDelta(baseline, this, encId, metadataSizes);
}
catch (Cci.PdbWritingException e)
{
diagnostics.Add(MessageProvider.CreateDiagnostic(MessageProvider.ERR_PdbWritingFailed, Location.None, e.Message));
return null;
}
catch (PermissionSetFileReadException e)
{
diagnostics.Add(MessageProvider.CreateDiagnostic(MessageProvider.ERR_PermissionSetAttributeFileReadError, Location.None, e.FileName, e.PropertyName, e.Message));
return null;
}
}
}
internal string Feature(string p)
{
if (_lazyFeatures == null)
......
......@@ -29,64 +29,6 @@ internal sealed class PdbWritingException : Exception
}
}
/// <summary>
/// This struct abstracts away the possible values for specifying the output information
/// for a PDB. It is legal to specify a file name, a stream or both. In the case both
/// are specified though the <see cref="Stream"/> value will be preferred.
/// </summary>
/// <remarks>
/// The file name is still used within the PDB writing code hence is not completely
/// redundant in the face of a <see cref="Stream"/> value.
/// </remarks>
internal struct PdbOutputInfo
{
internal static PdbOutputInfo None
{
get { return new PdbOutputInfo(); }
}
internal readonly string FileName;
internal readonly Stream Stream;
internal bool IsNone
{
get { return FileName == null && Stream == null; }
}
internal bool IsValid
{
get { return !IsNone; }
}
internal PdbOutputInfo(string fileName)
{
Debug.Assert(fileName != null);
FileName = fileName;
Stream = null;
}
internal PdbOutputInfo(Stream stream)
{
FileName = null;
Stream = stream;
}
internal PdbOutputInfo(string fileName, Stream stream)
{
Debug.Assert(fileName != null);
Debug.Assert(stream != null && stream.CanWrite);
FileName = fileName;
Stream = stream;
}
internal PdbOutputInfo WithStream(Stream stream)
{
return FileName != null
? new PdbOutputInfo(FileName, stream)
: new PdbOutputInfo(stream);
}
}
internal sealed class PdbWriter : IDisposable
{
internal const uint HiddenLocalAttributesValue = 1u;
......@@ -94,7 +36,8 @@ internal sealed class PdbWriter : IDisposable
private static Type s_lazyCorSymWriterSxSType;
private readonly PdbOutputInfo _pdbOutputInfo;
private readonly Func<Stream> _streamProvider;
private readonly string _fileName;
private readonly Func<object> _symWriterFactory;
private MetadataWriter _metadataWriter;
private ISymUnmanagedWriter2 _symWriter;
......@@ -111,10 +54,11 @@ internal sealed class PdbWriter : IDisposable
private uint[] _sequencePointEndLines;
private uint[] _sequencePointEndColumns;
public PdbWriter(PdbOutputInfo pdbOutputInfo, Func<object> symWriterFactory = null)
public PdbWriter(Func<Stream> streamProvider, string fileName, Func<object> symWriterFactory = null)
{
Debug.Assert(pdbOutputInfo.IsValid);
_pdbOutputInfo = pdbOutputInfo;
Debug.Assert(streamProvider != null);
_streamProvider = streamProvider;
_fileName = fileName;
_symWriterFactory = symWriterFactory;
CreateSequencePointBuffers(capacity: 64);
}
......@@ -131,8 +75,8 @@ public void Dispose()
}
/// <summary>
/// Close the PDB writer and write the contents to the location specified by the <see cref="PdbOutputInfo"/>
/// value. If a file name was specified this is the method which will cause it to be created.
/// Close the PDB writer and write the contents to the stream provided by <see cref="_streamProvider"/>
/// or file name specified by <see cref="_fileName"/> value if no stream has been provided.
/// </summary>
public void WritePdbToOutput()
{
......@@ -615,11 +559,14 @@ private static Type GetCorSymWriterSxSType()
public void SetMetadataEmitter(MetadataWriter metadataWriter)
{
Stream streamOpt = _streamProvider();
try
{
var instance = (ISymUnmanagedWriter2)(_symWriterFactory != null ? _symWriterFactory() : Activator.CreateInstance(GetCorSymWriterSxSType()));
var comStream = _pdbOutputInfo.Stream != null ? new ComStreamWrapper(_pdbOutputInfo.Stream) : null;
instance.Initialize(new PdbMetadataWrapper(metadataWriter), _pdbOutputInfo.FileName, comStream, fullBuild: true);
var comStream = (streamOpt != null) ? new ComStreamWrapper(streamOpt) : null;
instance.Initialize(new PdbMetadataWrapper(metadataWriter), _fileName, comStream, fullBuild: true);
_metadataWriter = metadataWriter;
_symWriter = instance;
......
......@@ -55,11 +55,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Emit
testData.Module = moduleBeingBuilt
End If
baseline = moduleBeingBuilt.PreviousGeneration
Dim definitionMap = moduleBeingBuilt.PreviousDefinitions
Dim changes = moduleBeingBuilt.Changes
Dim newBaseline As EmitBaseline = Nothing
If compilation.Compile(moduleBeingBuilt,
win32Resources:=Nothing,
xmlDocStream:=Nothing,
......@@ -71,43 +71,26 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Emit
' Map the definitions from the previous compilation to the current compilation.
' This must be done after compiling above since synthesized definitions
' (generated when compiling method bodies) may be required.
baseline = MapToCompilation(compilation, moduleBeingBuilt)
Dim pdbOutputInfo = New Cci.PdbOutputInfo(pdbName, pdbStream)
Using pdbWriter = New Cci.PdbWriter(pdbOutputInfo, If(testData IsNot Nothing, testData.SymWriterFactory, Nothing))
Dim context = New EmitContext(moduleBeingBuilt, Nothing, diagnostics)
Dim encId = Guid.NewGuid()
Try
Dim writer = New DeltaMetadataWriter(
context,
compilation.MessageProvider,
baseline,
encId,
definitionMap,
changes,
cancellationToken)
Dim metadataSizes As Cci.MetadataSizes = Nothing
writer.WriteMetadataAndIL(pdbWriter, metadataStream, ilStream, metadataSizes)
writer.GetMethodTokens(updatedMethods)
Dim hasErrors = diagnostics.HasAnyErrors()
Return New EmitDifferenceResult(
success:=Not hasErrors,
diagnostics:=diagnostics.ToReadOnlyAndFree(),
baseline:=If(hasErrors, Nothing, writer.GetDelta(baseline, compilation, encId, metadataSizes)))
Catch e As Cci.PdbWritingException
diagnostics.Add(ERRID.ERR_PDBWritingFailed, Location.None, e.Message)
Catch e As PermissionSetFileReadException
diagnostics.Add(ERRID.ERR_PermissionSetAttributeFileReadError, Location.None, e.FileName, e.PropertyName, e.Message)
End Try
End Using
Dim mappedBaseline = MapToCompilation(compilation, moduleBeingBuilt)
newBaseline = compilation.SerializeToDeltaStreams(
moduleBeingBuilt,
mappedBaseline,
definitionMap,
changes,
metadataStream,
ilStream,
pdbStream,
updatedMethods,
diagnostics,
testData?.SymWriterFactory,
cancellationToken)
End If
Return New EmitDifferenceResult(success:=False, diagnostics:=diagnostics.ToReadOnlyAndFree(), baseline:=Nothing)
Return New EmitDifferenceResult(
success:=newBaseline IsNot Nothing,
diagnostics:=diagnostics.ToReadOnlyAndFree(),
baseline:=newBaseline)
End Function
Friend Function MapToCompilation(
......
Imports Microsoft.CodeAnalysis.Test.Utilities
Imports System
Imports System.Collections.Generic
' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
Imports System.Collections.Immutable
Imports System.Linq
Imports System.Threading
Imports Xunit
Imports Microsoft.CodeAnalysis.Test.Utilities
Imports Roslyn.Test.Utilities
Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests.Emit
Public Class DeterministicTests : Inherits BasicTestBase
Public Class DeterministicTests
Inherits BasicTestBase
Private Function GetBytesEmitted(source As String, platform As Platform, debug As Boolean, deterministic As Boolean) As ImmutableArray(Of Byte)
Private Function GetBytesEmitted(source As String, platform As Platform, debug As Boolean) As ImmutableArray(Of Byte)
Dim options = If(debug, TestOptions.DebugExe, TestOptions.ReleaseExe).WithPlatform(platform)
If deterministic Then
options = options.WithFeatures({"dEtErmInIstIc"}.AsImmutable()) ' expect case-insensitivity
End If
options = options.WithFeatures({"dEtErmInIstIc"}.AsImmutable()) ' expect case-insensitivity
Dim compilation = CreateCompilationWithMscorlib({source}, assemblyName:="DeterminismTest", compOptions:=options)
......@@ -25,40 +23,36 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests.Emit
Return compilation.EmitToArray()
End Function
Private Class ImmutableByteArrayEqualityComparer : Implements IEqualityComparer(Of ImmutableArray(Of Byte))
Public Overloads Function Equals(x As ImmutableArray(Of Byte), y As ImmutableArray(Of Byte)) As Boolean Implements IEqualityComparer(Of ImmutableArray(Of Byte)).Equals
Return x.SequenceEqual(y)
End Function
Public Overloads Function GetHashCode(obj As ImmutableArray(Of Byte)) As Integer Implements IEqualityComparer(Of ImmutableArray(Of Byte)).GetHashCode
Return obj.GetHashCode()
End Function
End Class
<Fact>
Public Sub CompareAllBytesEmitted()
Public Sub CompareAllBytesEmitted_Release()
Dim source =
"Class Program
Shared Sub Main()
End Sub
End Class"
Dim comparer = New ImmutableByteArrayEqualityComparer()
Dim result1 = GetBytesEmitted(source, platform:=Platform.AnyCpu32BitPreferred, debug:=False)
Dim result2 = GetBytesEmitted(source, platform:=Platform.AnyCpu32BitPreferred, debug:=False)
AssertEx.Equal(result1, result2)
Dim result1 = GetBytesEmitted(source, platform:=Platform.AnyCpu32BitPreferred, debug:=True, deterministic:=True)
Dim result2 = GetBytesEmitted(source, platform:=Platform.AnyCpu32BitPreferred, debug:=True, deterministic:=True)
Assert.Equal(result1, result2, comparer)
Dim result3 = GetBytesEmitted(source, platform:=Platform.X64, debug:=False)
Dim result4 = GetBytesEmitted(source, platform:=Platform.X64, debug:=False)
AssertEx.Equal(result3, result4)
End Sub
Dim result3 = GetBytesEmitted(source, platform:=Platform.X64, debug:=False, deterministic:=True)
Dim result4 = GetBytesEmitted(source, platform:=Platform.X64, debug:=False, deterministic:=True)
Assert.Equal(result3, result4, comparer)
Assert.NotEqual(result1, result3, comparer)
<Fact(Skip:="https://github.com/dotnet/roslyn/issues/926"), WorkItem(926)>
Public Sub CompareAllBytesEmitted_Debug()
Dim source =
"Class Program
Shared Sub Main()
End Sub
End Class"
Dim result1 = GetBytesEmitted(source, platform:=Platform.AnyCpu32BitPreferred, debug:=True)
Dim result2 = GetBytesEmitted(source, platform:=Platform.AnyCpu32BitPreferred, debug:=True)
AssertEx.Equal(result1, result2)
Dim result5 = GetBytesEmitted(source, platform:=Platform.X64, debug:=False, deterministic:=False)
Dim result6 = GetBytesEmitted(source, platform:=Platform.X64, debug:=False, deterministic:=False)
Assert.NotEqual(result5, result6, comparer)
Assert.NotEqual(result3, result5, comparer)
Dim result3 = GetBytesEmitted(source, platform:=Platform.X64, debug:=True)
Dim result4 = GetBytesEmitted(source, platform:=Platform.X64, debug:=True)
AssertEx.Equal(result3, result4)
End Sub
End Class
End Namespace
......@@ -35,7 +35,7 @@ internal static void TestNotReusedOnAssemblyDiffers(string projectLanguage)
var metadataProject = context.CurrentSolution
.AddProject(projectId, "Metadata", "Metadata", LanguageNames.CSharp).GetProject(projectId)
.AddMetadataReference(TestReferences.NetFx.v4_0_30319.mscorlib)
.WithCompilationOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
.WithCompilationOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary, optimizationLevel: OptimizationLevel.Release));
var references = new List<MetadataReference>();
......
......@@ -637,16 +637,16 @@ public void TestThrowsOnGenerateNamespace()
using (var context = new TestContext())
{
Assert.Throws<ArgumentException>(() =>
{
try
{
context.GenerateSource(namespaceSymbol);
}
catch (AggregateException ae)
{
throw ae.InnerException;
}
});
{
try
{
context.GenerateSource(namespaceSymbol);
}
catch (AggregateException ae)
{
throw ae.InnerException;
}
});
}
}
......
......@@ -22,10 +22,11 @@ public static class CompilationExtensions
DiagnosticDescription[] expectedWarnings = null)
{
var stream = new MemoryStream();
MemoryStream pdbStream = (compilation.Options.OptimizationLevel == OptimizationLevel.Debug) ? new MemoryStream() : null;
var emitResult = compilation.Emit(
peStream: stream,
pdbStream: null,
pdbStream: pdbStream,
xmlDocumentationStream: null,
win32Resources: null,
manifestResources: null,
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册