diff --git a/src/Compilers/CSharp/Portable/Emitter/EditAndContinue/EmitHelpers.cs b/src/Compilers/CSharp/Portable/Emitter/EditAndContinue/EmitHelpers.cs
index b83f09914846ff282328877f4218659cfecef2d6..79bf8ece258bf2b257f7ff6252fafa8cbc43a8fe 100644
--- a/src/Compilers/CSharp/Portable/Emitter/EditAndContinue/EmitHelpers.cs
+++ b/src/Compilers/CSharp/Portable/Emitter/EditAndContinue/EmitHelpers.cs
@@ -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);
}
///
diff --git a/src/Compilers/CSharp/Test/Emit/Emit/DeterministicTests.cs b/src/Compilers/CSharp/Test/Emit/Emit/DeterministicTests.cs
index 6fcc2d4a6d3dde2a31b7d9c8331a95195941772c..0c9e60f9339150abdc60f3495ade1499fca5168a 100644
--- a/src/Compilers/CSharp/Test/Emit/Emit/DeterministicTests.cs
+++ b/src/Compilers/CSharp/Test/Emit/Emit/DeterministicTests.cs
@@ -32,13 +32,10 @@ private Guid CompiledGuid(string source, string assemblyName)
return result;
}
- private ImmutableArray GetBytesEmitted(string source, Platform platform, bool debug, bool deterministic)
+ private ImmutableArray 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 GetBytesEmitted(string source, Platform platform, b
return compilation.EmitToArray();
}
- private class ImmutableByteArrayEqualityComparer : IEqualityComparer>
- {
- public bool Equals(ImmutableArray x, ImmutableArray y)
- {
- return x.SequenceEqual(y);
- }
-
- public int GetHashCode(ImmutableArray 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]
diff --git a/src/Compilers/Core/Desktop/CommandLine/CommonCompiler.CompilerEmitStreamProvider.cs b/src/Compilers/Core/Desktop/CommandLine/CommonCompiler.CompilerEmitStreamProvider.cs
index f2ec0b0df1acfa9631109826ea08c05bd48e7380..4fc434be30370500b2a94e2fb241007e3e2d9af4 100644
--- a/src/Compilers/Core/Desktop/CommandLine/CommonCompiler.CompilerEmitStreamProvider.cs
+++ b/src/Compilers/Core/Desktop/CommandLine/CommonCompiler.CompilerEmitStreamProvider.cs
@@ -16,46 +16,46 @@ internal abstract partial class CommonCompiler
///
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)
{
diff --git a/src/Compilers/Core/Desktop/CommandLine/CommonCompiler.cs b/src/Compilers/Core/Desktop/CommandLine/CommonCompiler.cs
index 983cfdce143ee1e72ea93c907c1eecb6e31702aa..ea5ffa27caf72b17162a865ed2bb1fb43a0f8c71 100644
--- a/src/Compilers/Core/Desktop/CommandLine/CommonCompiler.cs
+++ b/src/Compilers/Core/Desktop/CommandLine/CommonCompiler.cs
@@ -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 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);
}
}
}
diff --git a/src/Compilers/Core/Portable/Compilation.EmitStreamProvider.cs b/src/Compilers/Core/Portable/Compilation.EmitStreamProvider.cs
index 71a9c2e556e5734785669a259166216dfed38403..804b027baf73327557216f22e5b83392303737da 100644
--- a/src/Compilers/Core/Portable/Compilation.EmitStreamProvider.cs
+++ b/src/Compilers/Core/Portable/Compilation.EmitStreamProvider.cs
@@ -35,6 +35,7 @@ internal sealed class SimpleEmitStreamProvider : EmitStreamProvider
internal SimpleEmitStreamProvider(Stream stream)
{
+ Debug.Assert(stream != null);
_stream = stream;
}
diff --git a/src/Compilers/Core/Portable/Compilation/Compilation.cs b/src/Compilers/Core/Portable/Compilation/Compilation.cs
index 9e8ba2067932e4caa985c25f455a095023ed5094..181162c204ce0e1e5eabe3cdca906c3cb4c13103 100644
--- a/src/Compilers/Core/Portable/Compilation/Compilation.cs
+++ b/src/Compilers/Core/Portable/Compilation/Compilation.cs
@@ -51,6 +51,8 @@ public abstract partial class Compilation
///
private SmallDictionary _lazyMakeMemberMissingMap;
+ private Dictionary _lazyFeatures;
+
internal Compilation(
string name,
ImmutableArray references,
@@ -1369,9 +1371,9 @@ internal void EnsureAnonymousTypeTemplates(CancellationToken cancellationToken)
/// Emit the IL for the compiled source code into the specified stream.
///
/// Provides the PE stream the compiler will write to.
- /// Provides the PDB stream the compiler will write to.
- /// Stream to which the compilation's XML documentation will be written. Null to forego XML generation.
- /// Stream from which the compilation's Win32 resources will be read (in RES format).
+ /// Provides the PDB stream the compiler will write to.
+ /// Stream to which the compilation's XML documentation will be written. Null to forego XML generation.
+ /// 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.
/// List of the compilation's managed resources. Null to indicate that there are none.
/// Emit options.
@@ -1379,19 +1381,19 @@ internal void EnsureAnonymousTypeTemplates(CancellationToken cancellationToken)
/// To cancel the emit process.
internal EmitResult Emit(
EmitStreamProvider peStreamProvider,
- Cci.PdbOutputInfo pdbOutputInfo,
- Stream xmlDocumentationStream = null,
- Stream win32Resources = null,
- IEnumerable manifestResources = null,
- EmitOptions options = null,
- Func> getHostDiagnostics = null,
- CancellationToken cancellationToken = default(CancellationToken))
+ EmitStreamProvider pdbStreamProvider,
+ EmitStreamProvider xmlDocumentationStreamProvider,
+ EmitStreamProvider win32ResourcesProvider,
+ IEnumerable manifestResources,
+ EmitOptions options,
+ Func> 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);
}
+ ///
+ /// This overload is only intended to be directly called by tests that want to pass .
+ /// The map is used for storing a list of methods and their associated IL.
+ ///
+ /// True if emit succeeded.
+ internal EmitResult Emit(
+ Stream peStream,
+ Stream pdbStream,
+ Stream xmlDocumentationStream,
+ Stream win32Resources,
+ IEnumerable manifestResources,
+ EmitOptions options,
+ CompilationTestData testData,
+ Func> 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);
+ }
+
///
/// 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 updatedMethodHandles,
CompilationTestData testData,
CancellationToken cancellationToken);
-
- ///
- /// This overload is only intended to be directly called by tests that want to pass .
- /// The map is used for storing a list of methods and their associated IL.
- ///
- /// True if emit succeeded.
- internal EmitResult Emit(
- Stream peStream,
- Stream pdbStream,
- Stream xmlDocumentationStream,
- Stream win32Resources,
- IEnumerable manifestResources,
- EmitOptions options,
- CompilationTestData testData,
- Func> getHostDiagnostics,
- CancellationToken cancellationToken)
- {
- return Emit(
- new SimpleEmitStreamProvider(peStream),
- new Cci.PdbOutputInfo(pdbStream),
- new SimpleEmitStreamProvider(xmlDocumentationStream),
- new SimpleEmitStreamProvider(win32Resources),
- manifestResources,
- options,
- testData,
- getHostDiagnostics,
- cancellationToken);
- }
-
+
///
/// This overload is only intended to be directly called by tests that want to pass .
/// The map is used for storing a list of methods and their associated IL.
@@ -1517,7 +1519,7 @@ internal void EnsureAnonymousTypeTemplates(CancellationToken cancellationToken)
/// True if emit succeeded.
internal EmitResult Emit(
EmitStreamProvider peStreamProvider,
- Cci.PdbOutputInfo pdbOutputInfo,
+ EmitStreamProvider pdbStreamProvider,
EmitStreamProvider xmlDocumentationStreamProvider,
EmitStreamProvider win32ResourcesStreamProvider,
IEnumerable 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.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