parent
f061d328d9
commit
3517ecda2f
|
|
@ -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":
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue