Ensure DebugType = embedded and DebugType = none work

Fixes #7406
This commit is contained in:
Pranav K 2018-03-02 14:36:42 -08:00
parent f061d328d9
commit 3517ecda2f
4 changed files with 163 additions and 21 deletions

View File

@ -25,6 +25,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
private CSharpParseOptions _parseOptions; private CSharpParseOptions _parseOptions;
private CSharpCompilationOptions _compilationOptions; private CSharpCompilationOptions _compilationOptions;
private EmitOptions _emitOptions; private EmitOptions _emitOptions;
private bool _emitPdb;
public CSharpCompiler(RazorReferenceManager manager, IHostingEnvironment hostingEnvironment) public CSharpCompiler(RazorReferenceManager manager, IHostingEnvironment hostingEnvironment)
{ {
@ -50,6 +51,15 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
} }
} }
public virtual bool EmitPdb
{
get
{
EnsureOptions();
return _emitPdb;
}
}
public virtual EmitOptions EmitOptions public virtual EmitOptions EmitOptions
{ {
get get
@ -105,6 +115,8 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
private EmitOptions GetEmitOptions(DependencyContextCompilationOptions dependencyContextOptions) private EmitOptions GetEmitOptions(DependencyContextCompilationOptions dependencyContextOptions)
{ {
// Assume we're always producing pdbs unless DebugType = none
_emitPdb = true;
DebugInformationFormat debugInformationFormat; DebugInformationFormat debugInformationFormat;
if (string.IsNullOrEmpty(dependencyContextOptions.DebugType)) if (string.IsNullOrEmpty(dependencyContextOptions.DebugType))
{ {
@ -117,11 +129,18 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
// Based on https://github.com/dotnet/roslyn/blob/1d28ff9ba248b332de3c84d23194a1d7bde07e4d/src/Compilers/CSharp/Portable/CommandLine/CSharpCommandLineParser.cs#L624-L640 // Based on https://github.com/dotnet/roslyn/blob/1d28ff9ba248b332de3c84d23194a1d7bde07e4d/src/Compilers/CSharp/Portable/CommandLine/CSharpCommandLineParser.cs#L624-L640
switch (dependencyContextOptions.DebugType.ToLower()) switch (dependencyContextOptions.DebugType.ToLower())
{ {
case "none":
// There isn't a way to represent none in DebugInformationFormat.
// We'll set EmitPdb to false and let callers handle it by setting a null pdb-stream.
_emitPdb = false;
return new EmitOptions();
case "portable": case "portable":
debugInformationFormat = DebugInformationFormat.PortablePdb; debugInformationFormat = DebugInformationFormat.PortablePdb;
break; break;
case "embedded": case "embedded":
debugInformationFormat = DebugInformationFormat.Embedded; // Roslyn does not expose enough public APIs to produce a binary with embedded pdbs.
// We'll produce PortablePdb instead to continue providing a reasonable user experience.
debugInformationFormat = DebugInformationFormat.PortablePdb;
break; break;
case "full": case "full":
case "pdbonly": case "pdbonly":

View File

@ -14,6 +14,7 @@ using Microsoft.AspNetCore.Mvc.Razor.Compilation;
using Microsoft.AspNetCore.Razor.Hosting; using Microsoft.AspNetCore.Razor.Hosting;
using Microsoft.AspNetCore.Razor.Language; using Microsoft.AspNetCore.Razor.Language;
using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Emit;
using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.Text;
using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.FileProviders;
@ -133,7 +134,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
return cachedResult; return cachedResult;
} }
var normalizedPath = GetNormalizedPath(relativePath); var normalizedPath = GetNormalizedPath(relativePath);
if (_cache.TryGetValue(normalizedPath, out cachedResult)) if (_cache.TryGetValue(normalizedPath, out cachedResult))
{ {
return cachedResult; return cachedResult;
@ -374,13 +375,16 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
var assemblyName = Path.GetRandomFileName(); var assemblyName = Path.GetRandomFileName();
var compilation = CreateCompilation(generatedCode, assemblyName); var compilation = CreateCompilation(generatedCode, assemblyName);
var emitOptions = _csharpCompiler.EmitOptions;
var emitPdbFile = _csharpCompiler.EmitPdb && emitOptions.DebugInformationFormat != DebugInformationFormat.Embedded;
using (var assemblyStream = new MemoryStream()) using (var assemblyStream = new MemoryStream())
using (var pdbStream = new MemoryStream()) using (var pdbStream = emitPdbFile ? new MemoryStream() : null)
{ {
var result = compilation.Emit( var result = compilation.Emit(
assemblyStream, assemblyStream,
pdbStream, pdbStream,
options: _csharpCompiler.EmitOptions); options: emitOptions);
if (!result.Success) if (!result.Success)
{ {
@ -392,9 +396,9 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
} }
assemblyStream.Seek(0, SeekOrigin.Begin); assemblyStream.Seek(0, SeekOrigin.Begin);
pdbStream.Seek(0, SeekOrigin.Begin); pdbStream?.Seek(0, SeekOrigin.Begin);
var assembly = Assembly.Load(assemblyStream.ToArray(), pdbStream.ToArray()); var assembly = Assembly.Load(assemblyStream.ToArray(), pdbStream?.ToArray());
_logger.GeneratedCodeToAssemblyCompilationEnd(codeDocument.Source.FilePath, startTimestamp); _logger.GeneratedCodeToAssemblyCompilationEnd(codeDocument.Source.FilePath, startTimestamp);
return assembly; return assembly;

View File

@ -174,10 +174,8 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
} }
[Theory] [Fact]
[InlineData("portable", DebugInformationFormat.PortablePdb)] public void EmitOptions_ReadsDebugTypeFromDependencyContext()
[InlineData("embedded", DebugInformationFormat.Embedded)]
public void EmitOptions_ReadsDebugTypeFromDependencyContext(string debugType, DebugInformationFormat expected)
{ {
// Arrange // Arrange
var dependencyContextOptions = new DependencyContextCompilationOptions( var dependencyContextOptions = new DependencyContextCompilationOptions(
@ -190,7 +188,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
keyFile: null, keyFile: null,
delaySign: null, delaySign: null,
publicSign: null, publicSign: null,
debugType: debugType, debugType: "portable",
emitEntryPoint: null, emitEntryPoint: null,
generateXmlDocumentation: null); generateXmlDocumentation: null);
var referenceManager = Mock.Of<RazorReferenceManager>(); var referenceManager = Mock.Of<RazorReferenceManager>();
@ -200,7 +198,62 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
// Act & Assert // Act & Assert
var emitOptions = compiler.EmitOptions; var emitOptions = compiler.EmitOptions;
Assert.Equal(expected, emitOptions.DebugInformationFormat); Assert.Equal(DebugInformationFormat.PortablePdb, emitOptions.DebugInformationFormat);
Assert.True(compiler.EmitPdb);
}
[Fact]
public void EmitOptions_SetsDebugInformationFormatToPortable_WhenDebugTypeIsEmbedded()
{
// Arrange
var dependencyContextOptions = new DependencyContextCompilationOptions(
new[] { "MyDefine" },
languageVersion: "7.1",
platform: null,
allowUnsafe: true,
warningsAsErrors: null,
optimize: null,
keyFile: null,
delaySign: null,
publicSign: null,
debugType: "embedded",
emitEntryPoint: null,
generateXmlDocumentation: null);
var referenceManager = Mock.Of<RazorReferenceManager>();
var hostingEnvironment = Mock.Of<IHostingEnvironment>();
var compiler = new TestCSharpCompiler(referenceManager, hostingEnvironment, dependencyContextOptions);
// Act & Assert
var emitOptions = compiler.EmitOptions;
Assert.Equal(DebugInformationFormat.PortablePdb, emitOptions.DebugInformationFormat);
Assert.True(compiler.EmitPdb);
}
[Fact]
public void EmitOptions_DoesNotSetEmitPdb_IfDebugTypeIsNone()
{
// Arrange
var dependencyContextOptions = new DependencyContextCompilationOptions(
new[] { "MyDefine" },
languageVersion: "7.1",
platform: null,
allowUnsafe: true,
warningsAsErrors: null,
optimize: null,
keyFile: null,
delaySign: null,
publicSign: null,
debugType: "none",
emitEntryPoint: null,
generateXmlDocumentation: null);
var referenceManager = Mock.Of<RazorReferenceManager>();
var hostingEnvironment = Mock.Of<IHostingEnvironment>();
var compiler = new TestCSharpCompiler(referenceManager, hostingEnvironment, dependencyContextOptions);
// Act & Assert
Assert.False(compiler.EmitPdb);
} }
[Fact] [Fact]

View File

@ -12,6 +12,7 @@ using Microsoft.AspNetCore.Mvc.Razor.Extensions;
using Microsoft.AspNetCore.Razor.Hosting; using Microsoft.AspNetCore.Razor.Hosting;
using Microsoft.AspNetCore.Razor.Language; using Microsoft.AspNetCore.Razor.Language;
using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Emit;
using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Moq; using Moq;
@ -799,11 +800,52 @@ this should fail";
Assert.NotNull(result); Assert.NotNull(result);
} }
[Fact]
public void CompileAndEmit_DoesNotThrowIfDebugTypeIsEmbedded()
{
// Arrange
var referenceManager = CreateReferenceManager(Options.Create(new RazorViewEngineOptions()));
var csharpCompiler = new TestCSharpCompiler(referenceManager, Mock.Of<IHostingEnvironment>())
{
EmitOptionsSettable = new EmitOptions(debugInformationFormat: DebugInformationFormat.Embedded),
};
var compiler = GetViewCompiler(csharpCompiler: csharpCompiler);
var codeDocument = RazorCodeDocument.Create(RazorSourceDocument.Create("Hello world", "some-relative-path.cshtml"));
// Act
var result = compiler.CompileAndEmit(codeDocument, "public class Test{}");
// Assert
Assert.NotNull(result);
}
[Fact]
public void CompileAndEmit_WorksIfEmitPdbIsNotSet()
{
// Arrange
var referenceManager = CreateReferenceManager(Options.Create(new RazorViewEngineOptions()));
var csharpCompiler = new TestCSharpCompiler(referenceManager, Mock.Of<IHostingEnvironment>())
{
EmitPdbSettable = false,
};
var compiler = GetViewCompiler(csharpCompiler: csharpCompiler);
var codeDocument = RazorCodeDocument.Create(RazorSourceDocument.Create("Hello world", "some-relative-path.cshtml"));
// Act
var result = compiler.CompileAndEmit(codeDocument, "public class Test{}");
// Assert
Assert.NotNull(result);
}
private static TestRazorViewCompiler GetViewCompiler( private static TestRazorViewCompiler GetViewCompiler(
TestFileProvider fileProvider = null, TestFileProvider fileProvider = null,
Action<RoslynCompilationContext> compilationCallback = null, Action<RoslynCompilationContext> compilationCallback = null,
RazorReferenceManager referenceManager = null, RazorReferenceManager referenceManager = null,
IList<CompiledViewDescriptor> precompiledViews = null) IList<CompiledViewDescriptor> precompiledViews = null,
CSharpCompiler csharpCompiler = null)
{ {
fileProvider = fileProvider ?? new TestFileProvider(); fileProvider = fileProvider ?? new TestFileProvider();
var accessor = Mock.Of<IRazorViewEngineFileProviderAccessor>(a => a.FileProvider == fileProvider); var accessor = Mock.Of<IRazorViewEngineFileProviderAccessor>(a => a.FileProvider == fileProvider);
@ -812,12 +854,7 @@ this should fail";
var options = Options.Create(new RazorViewEngineOptions()); var options = Options.Create(new RazorViewEngineOptions());
if (referenceManager == null) if (referenceManager == null)
{ {
var applicationPartManager = new ApplicationPartManager(); referenceManager = CreateReferenceManager(options);
var assembly = typeof(RazorViewCompilerTest).Assembly;
applicationPartManager.ApplicationParts.Add(new AssemblyPart(assembly));
applicationPartManager.FeatureProviders.Add(new MetadataReferenceFeatureProvider());
referenceManager = new DefaultRazorReferenceManager(applicationPartManager, options);
} }
precompiledViews = precompiledViews ?? Array.Empty<CompiledViewDescriptor>(); precompiledViews = precompiledViews ?? Array.Empty<CompiledViewDescriptor>();
@ -828,15 +865,28 @@ this should fail";
{ {
RazorExtensions.Register(builder); RazorExtensions.Register(builder);
}); });
csharpCompiler = csharpCompiler ?? new CSharpCompiler(referenceManager, hostingEnvironment);
var viewCompiler = new TestRazorViewCompiler( var viewCompiler = new TestRazorViewCompiler(
fileProvider, fileProvider,
projectEngine, projectEngine,
new CSharpCompiler(referenceManager, hostingEnvironment), csharpCompiler,
compilationCallback, compilationCallback,
precompiledViews); precompiledViews);
return viewCompiler; return viewCompiler;
} }
private static RazorReferenceManager CreateReferenceManager(IOptions<RazorViewEngineOptions> options)
{
var applicationPartManager = new ApplicationPartManager();
var assembly = typeof(RazorViewCompilerTest).Assembly;
applicationPartManager.ApplicationParts.Add(new AssemblyPart(assembly));
applicationPartManager.FeatureProviders.Add(new MetadataReferenceFeatureProvider());
return new DefaultRazorReferenceManager(applicationPartManager, options);
}
private class TestRazorViewCompiler : RazorViewCompiler private class TestRazorViewCompiler : RazorViewCompiler
{ {
public TestRazorViewCompiler( public TestRazorViewCompiler(
@ -866,5 +916,21 @@ this should fail";
return Compile(relativePath); return Compile(relativePath);
} }
} }
private class TestCSharpCompiler : CSharpCompiler
{
public TestCSharpCompiler(RazorReferenceManager manager, IHostingEnvironment hostingEnvironment)
: base(manager, hostingEnvironment)
{
}
public EmitOptions EmitOptionsSettable { get; set; }
public bool EmitPdbSettable { get; set; }
public override EmitOptions EmitOptions => EmitOptionsSettable;
public override bool EmitPdb => EmitPdbSettable;
}
} }
} }