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