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 CSharpCompilationOptions _compilationOptions;
private EmitOptions _emitOptions;
private bool _emitPdb;
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
{
get
@ -105,6 +115,8 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
private EmitOptions GetEmitOptions(DependencyContextCompilationOptions dependencyContextOptions)
{
// Assume we're always producing pdbs unless DebugType = none
_emitPdb = true;
DebugInformationFormat debugInformationFormat;
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
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":
debugInformationFormat = DebugInformationFormat.PortablePdb;
break;
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;
case "full":
case "pdbonly":

View File

@ -14,6 +14,7 @@ using Microsoft.AspNetCore.Mvc.Razor.Compilation;
using Microsoft.AspNetCore.Razor.Hosting;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Emit;
using Microsoft.CodeAnalysis.Text;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.FileProviders;
@ -133,7 +134,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
return cachedResult;
}
var normalizedPath = GetNormalizedPath(relativePath);
var normalizedPath = GetNormalizedPath(relativePath);
if (_cache.TryGetValue(normalizedPath, out cachedResult))
{
return cachedResult;
@ -325,7 +326,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
// error message; for now, lets just be extra protective and assume 0 imports to not give a bad error.
var imports = importFeature?.GetImports(projectItem) ?? Enumerable.Empty<RazorProjectItem>();
var physicalImports = imports.Where(import => import.FilePath != null);
// Now that we have non-dynamic imports we need to get their RazorProjectItem equivalents so we have their
// physical file paths (according to the FileSystem).
foreach (var physicalImport in physicalImports)
@ -374,13 +375,16 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
var assemblyName = Path.GetRandomFileName();
var compilation = CreateCompilation(generatedCode, assemblyName);
var emitOptions = _csharpCompiler.EmitOptions;
var emitPdbFile = _csharpCompiler.EmitPdb && emitOptions.DebugInformationFormat != DebugInformationFormat.Embedded;
using (var assemblyStream = new MemoryStream())
using (var pdbStream = new MemoryStream())
using (var pdbStream = emitPdbFile ? new MemoryStream() : null)
{
var result = compilation.Emit(
assemblyStream,
pdbStream,
options: _csharpCompiler.EmitOptions);
options: emitOptions);
if (!result.Success)
{
@ -392,9 +396,9 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
}
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);
return assembly;

View File

@ -174,10 +174,8 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
}
[Theory]
[InlineData("portable", DebugInformationFormat.PortablePdb)]
[InlineData("embedded", DebugInformationFormat.Embedded)]
public void EmitOptions_ReadsDebugTypeFromDependencyContext(string debugType, DebugInformationFormat expected)
[Fact]
public void EmitOptions_ReadsDebugTypeFromDependencyContext()
{
// Arrange
var dependencyContextOptions = new DependencyContextCompilationOptions(
@ -190,7 +188,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
keyFile: null,
delaySign: null,
publicSign: null,
debugType: debugType,
debugType: "portable",
emitEntryPoint: null,
generateXmlDocumentation: null);
var referenceManager = Mock.Of<RazorReferenceManager>();
@ -200,7 +198,62 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
// Act & Assert
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]

View File

@ -12,6 +12,7 @@ using Microsoft.AspNetCore.Mvc.Razor.Extensions;
using Microsoft.AspNetCore.Razor.Hosting;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Emit;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Moq;
@ -799,11 +800,52 @@ this should fail";
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(
TestFileProvider fileProvider = null,
Action<RoslynCompilationContext> compilationCallback = null,
RazorReferenceManager referenceManager = null,
IList<CompiledViewDescriptor> precompiledViews = null)
IList<CompiledViewDescriptor> precompiledViews = null,
CSharpCompiler csharpCompiler = null)
{
fileProvider = fileProvider ?? new TestFileProvider();
var accessor = Mock.Of<IRazorViewEngineFileProviderAccessor>(a => a.FileProvider == fileProvider);
@ -812,12 +854,7 @@ this should fail";
var options = Options.Create(new RazorViewEngineOptions());
if (referenceManager == null)
{
var applicationPartManager = new ApplicationPartManager();
var assembly = typeof(RazorViewCompilerTest).Assembly;
applicationPartManager.ApplicationParts.Add(new AssemblyPart(assembly));
applicationPartManager.FeatureProviders.Add(new MetadataReferenceFeatureProvider());
referenceManager = new DefaultRazorReferenceManager(applicationPartManager, options);
referenceManager = CreateReferenceManager(options);
}
precompiledViews = precompiledViews ?? Array.Empty<CompiledViewDescriptor>();
@ -828,15 +865,28 @@ this should fail";
{
RazorExtensions.Register(builder);
});
csharpCompiler = csharpCompiler ?? new CSharpCompiler(referenceManager, hostingEnvironment);
var viewCompiler = new TestRazorViewCompiler(
fileProvider,
projectEngine,
new CSharpCompiler(referenceManager, hostingEnvironment),
csharpCompiler,
compilationCallback,
precompiledViews);
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
{
public TestRazorViewCompiler(
@ -866,5 +916,21 @@ this should fail";
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;
}
}
}