提交 93904ecf 编写于 作者: T Ty Overby

Handle PE writing exceptions by appending a diagnostic

上级 16faf273
......@@ -4675,4 +4675,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<data name="ERR_TooManyUserStrings" xml:space="preserve">
<value>Combined length of user strings used by the program exceeds allowed limit. Try to decrease use of string literals.</value>
</data>
</root>
\ No newline at end of file
<data name="ERR_PeWritingFailure" xml:space="preserve">
<value>An error occurred while writing the output file.</value>
</data>
</root>
......@@ -1320,5 +1320,6 @@ internal enum ErrorCode
ERR_InvalidPathMap = 8101,
ERR_PublicSignButNoKey = 8102,
ERR_TooManyUserStrings = 8103,
ERR_PeWritingFailure = 8104,
}
}
......@@ -202,6 +202,7 @@ public override void ReportDuplicateMetadataReferenceWeak(DiagnosticBag diagnost
public override int ERR_MetadataNameTooLong { get { return (int)ErrorCode.ERR_MetadataNameTooLong; } }
public override int ERR_EncReferenceToAddedMember { get { return (int)ErrorCode.ERR_EncReferenceToAddedMember; } }
public override int ERR_TooManyUserStrings { get { return (int)ErrorCode.ERR_TooManyUserStrings; } }
public override int ERR_PeWritingFailure { get { return (int)ErrorCode.ERR_PeWritingFailure; } }
public override void ReportInvalidAttributeArgument(DiagnosticBag diagnostics, SyntaxNode attributeSyntax, int parameterIndex, AttributeData attribute)
{
......
......@@ -54,8 +54,8 @@ class C
options: options);
comp.VerifyEmitDiagnostics(
// error CS7028: Error signing output with public key from container 'RoslynTestContainer' -- This is a test IOException
Diagnostic(ErrorCode.ERR_PublicKeyContainerFailure).WithArguments("RoslynTestContainer", "This is a test IOException").WithLocation(1, 1));
// error CS8104: An error occurred while writing the Portable Executable file.
Diagnostic(ErrorCode.ERR_PeWritingFailure).WithArguments("This is a test IOException").WithLocation(1, 1));
}
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using System.Reflection;
......@@ -2699,10 +2698,16 @@ public void BrokenOutStream()
var compilation = CreateCompilationWithMscorlib(source);
var output = new BrokenStream();
Assert.Throws<IOException>(() => compilation.Emit(output));
var result = compilation.Emit(output);
result.Diagnostics.Verify(
// error CS8104: An error occurred while writing the Portable Executable file.
Diagnostic(ErrorCode.ERR_PeWritingFailure).WithArguments("I/O error occurred.").WithLocation(1, 1));
output.BreakHow = 1;
Assert.Throws<NotSupportedException>(() => compilation.Emit(output));
result = compilation.Emit(output);
result.Diagnostics.Verify(
// error CS8104: An error occurred while writing the Portable Executable file.
Diagnostic(ErrorCode.ERR_PeWritingFailure).WithArguments("Specified method is not supported.").WithLocation(1, 1));
// disposed stream is not writable
var outReal = new MemoryStream();
......@@ -2949,5 +2954,27 @@ static int M(dynamic d, object o)
AssertEx.Equal(expectedNames, actualNames);
}
}
[Fact]
[WorkItem(3240, "https://github.com/dotnet/roslyn/pull/8227")]
public void FailingEmitter()
{
string source = @"
public class X
{
public static void Main()
{
}
}";
var compilation = CreateCompilationWithMscorlib(source);
var broken = new BrokenStream();
broken.BreakHow = 0;
var result = compilation.Emit(broken);
Assert.False(result.Success);
result.Diagnostics.Verify(
// error CS8104: An error occurred while writing the Portable Executable file.
Diagnostic(ErrorCode.ERR_PeWritingFailure).WithArguments("I/O error occurred.").WithLocation(1, 1));
}
}
}
......@@ -20,71 +20,4 @@ public Stream GetXmlInclude(SyntaxTree syntaxTree, string xmlIncludeFile)
throw new NotImplementedException();
}
}
internal class BrokenStream : Stream
{
public int BreakHow;
public override bool CanRead
{
get { return true; }
}
public override bool CanSeek
{
get { return true; }
}
public override bool CanWrite
{
get { return true; }
}
public override void Flush()
{
}
public override long Length
{
get
{
return 0;
}
}
public override long Position
{
get
{
return 0;
}
set
{
if (BreakHow == 1)
throw new NotSupportedException();
}
}
public override int Read(byte[] buffer, int offset, int count)
{
return 0;
}
public override long Seek(long offset, SeekOrigin origin)
{
return 0;
}
public override void SetLength(long value)
{
if (BreakHow == 2)
throw new IOException();
}
public override void Write(byte[] buffer, int offset, int count)
{
if (BreakHow == 0)
throw new IOException();
}
}
}
......@@ -1844,17 +1844,8 @@ private static EmitResult ToEmitResultAndFree(DiagnosticBag diagnostics, bool su
{
Debug.Assert(Options.StrongNameProvider != null);
// Targeted try-catch for errors during CreateInputStream as found in TFS 1140649
// TODO: Put this wrapping in PeWriter to catch all potential PE writing exceptions
try
{
signingInputStream = Options.StrongNameProvider.CreateInputStream();
retStream = signingInputStream;
}
catch (Exception e)
{
throw new Cci.PeWritingException(e);
}
signingInputStream = Options.StrongNameProvider.CreateInputStream();
retStream = signingInputStream;
}
else
{
......@@ -1917,9 +1908,8 @@ private static EmitResult ToEmitResultAndFree(DiagnosticBag diagnostics, bool su
}
catch (Cci.PeWritingException e)
{
// Targeted fix for TFS 1140649
// TODO: Add resource and better error message for a variety of PE exceptions
diagnostics.Add(StrongNameKeys.GetError(StrongNameKeys.KeyFilePath, StrongNameKeys.KeyContainer, e.Message, MessageProvider));
diagnostics.Add(MessageProvider.CreateDiagnostic(MessageProvider.ERR_PeWritingFailure, Location.None, e.Message));
return false;
}
catch (ResourceException e)
{
......
......@@ -203,6 +203,7 @@ public DiagnosticInfo FilterDiagnosticInfo(DiagnosticInfo diagnosticInfo, Compil
public abstract int ERR_MetadataNameTooLong { get; }
public abstract int ERR_EncReferenceToAddedMember { get; }
public abstract int ERR_TooManyUserStrings { get; }
public abstract int ERR_PeWritingFailure { get; }
public abstract void ReportInvalidAttributeArgument(DiagnosticBag diagnostics, SyntaxNode attributeSyntax, int parameterIndex, AttributeData attribute);
public abstract void ReportInvalidNamedArgument(DiagnosticBag diagnostics, SyntaxNode attributeSyntax, int namedArgumentIndex, ITypeSymbol attributeClass, string parameterName);
......
......@@ -12,6 +12,7 @@
using Microsoft.CodeAnalysis;
using Roslyn.Utilities;
using EmitContext = Microsoft.CodeAnalysis.Emit.EmitContext;
using Microsoft.CodeAnalysis.CodeGen;
namespace Microsoft.Cci
{
......@@ -88,10 +89,17 @@ internal sealed class PeWriter
// If PDB writer is given, we have to have PDB path.
Debug.Assert(nativePdbWriterOpt == null || pdbPathOpt != null);
var peWriter = new PeWriter(context.Module.Properties, context.Module.Win32Resources, context.Module.Win32ResourceSection, pdbPathOpt, deterministic);
var mdWriter = FullMetadataWriter.Create(context, messageProvider, allowMissingMethodBodies, deterministic, getPortablePdbStreamOpt != null, cancellationToken);
try
{
var peWriter = new PeWriter(context.Module.Properties, context.Module.Win32Resources, context.Module.Win32ResourceSection, pdbPathOpt, deterministic);
var mdWriter = FullMetadataWriter.Create(context, messageProvider, allowMissingMethodBodies, deterministic, getPortablePdbStreamOpt != null, cancellationToken);
return peWriter.WritePeToStream(mdWriter, getPeStream, getPortablePdbStreamOpt, nativePdbWriterOpt);
return peWriter.WritePeToStream(mdWriter, getPeStream, getPortablePdbStreamOpt, nativePdbWriterOpt);
}
catch (Exception ex) when (!(ex is PdbWritingException || ex is ResourceException || ex is PermissionSetFileReadException))
{
throw new PeWritingException(ex);
}
}
private bool WritePeToStream(MetadataWriter mdWriter, Func<Stream> getPeStream, Func<Stream> getPortablePdbStreamOpt, PdbWriter nativePdbWriterOpt)
......
......@@ -1682,6 +1682,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
ERR_InvalidPathMap = 37253
ERR_PublicSignNoKey = 37254
ERR_TooManyUserStrings = 37255
ERR_PeWritingFailure = 37256
ERR_LastPlusOne
......
......@@ -480,6 +480,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
Return ERRID.ERR_TooManyUserStrings
End Get
End Property
Public Overrides ReadOnly Property ERR_PeWritingFailure As Integer
Get
Return ERRID.ERR_PeWritingFailure
End Get
End Property
End Class
End Namespace
......@@ -5347,4 +5347,7 @@
<data name="ERR_TooManyUserStrings" xml:space="preserve">
<value>Combined length of user strings used by the program exceeds allowed limit. Try to decrease use of string or XML literals.</value>
</data>
</root>
\ No newline at end of file
<data name="ERR_PeWritingFailure" xml:space="preserve">
<value>An error occurred while writing the output file.</value>
</data>
</root>
......@@ -54,7 +54,8 @@ End Class
</compilation>, options:=options)
comp.VerifyEmitDiagnostics(
Diagnostic(ERRID.ERR_PublicKeyContainerFailure).WithArguments("RoslynTestContainer", "This is a test IOException").WithLocation(1, 1))
Diagnostic(ERRID.ERR_PeWritingFailure).WithArguments("This is a test IOException").WithLocation(1, 1))
End Sub
End Class
......@@ -2923,5 +2923,89 @@ End Class
End Using
End Sub
Private Class FailingStream
Inherits Stream
Shared exception As IOException = New IOException()
Public Overrides ReadOnly Property CanRead As Boolean
Get
Return True
End Get
End Property
Public Overrides ReadOnly Property CanSeek As Boolean
Get
Return True
End Get
End Property
Public Overrides ReadOnly Property CanWrite As Boolean
Get
Return True
End Get
End Property
Public Overrides ReadOnly Property Length As Long
Get
Throw exception
End Get
End Property
Public Overrides Property Position As Long
Get
Throw exception
End Get
Set(value As Long)
Throw exception
End Set
End Property
Public Overrides Sub Flush()
Throw exception
End Sub
Public Overrides Sub SetLength(value As Long)
Throw exception
End Sub
Public Overrides Sub Write(buffer() As Byte, offset As Integer, count As Integer)
Throw exception
End Sub
Public Overrides Function Read(buffer() As Byte, offset As Integer, count As Integer) As Integer
Throw exception
End Function
Public Overrides Function Seek(offset As Long, origin As SeekOrigin) As Long
Throw exception
End Function
End Class
<Fact>
Public Sub FailingEmitter()
' Check that Compilation.Emit actually produces compilation errors.
Dim compilation = CompilationUtils.CreateCompilationWithMscorlibAndVBRuntime(
<compilation>
<file name="a.vb">
Module M1
Sub Main()
End Sub
End Module
</file>
</compilation>)
Dim emitResult As EmitResult
Using output = New BrokenStream()
output.BreakHow = 0
emitResult = compilation.Emit(output, Nothing, Nothing, Nothing)
End Using
CompilationUtils.AssertTheseDiagnostics(emitResult.Diagnostics,
<expected>
BC37256: An error occurred while writing the Portable Executable file.
</expected>)
End Sub
End Class
End Namespace
......@@ -362,5 +362,13 @@ public override int ERR_TooManyUserStrings
throw new NotImplementedException();
}
}
public override int ERR_PeWritingFailure
{
get
{
throw new NotImplementedException();
}
}
}
}
// 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.IO;
namespace Roslyn.Test.Utilities
{
internal class BrokenStream : Stream
{
public int BreakHow;
public override bool CanRead
{
get { return true; }
}
public override bool CanSeek
{
get { return true; }
}
public override bool CanWrite
{
get { return true; }
}
public override void Flush()
{
}
public override long Length
{
get
{
return 0;
}
}
public override long Position
{
get
{
return 0;
}
set
{
if (BreakHow == 1)
throw new NotSupportedException();
}
}
public override int Read(byte[] buffer, int offset, int count)
{
return 0;
}
public override long Seek(long offset, SeekOrigin origin)
{
return 0;
}
public override void SetLength(long value)
{
if (BreakHow == 2)
throw new IOException();
}
public override void Write(byte[] buffer, int offset, int count)
{
if (BreakHow == 0)
throw new IOException();
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册