Skip to content
This repository has been archived by the owner on Dec 14, 2018. It is now read-only.

Commit

Permalink
Avoid holding on to DependencyContext instance
Browse files Browse the repository at this point in the history
Fixes #4527
  • Loading branch information
pranavkm committed Apr 29, 2016
1 parent cba4d1d commit 0ff2f87
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 66 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,12 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
/// </summary>
public class DefaultRoslynCompilationService : ICompilationService
{
private readonly IHostingEnvironment _hostingEnvironment;
private readonly IFileProvider _fileProvider;
private readonly Action<RoslynCompilationContext> _compilationCallback;
private readonly CSharpParseOptions _parseOptions;
private readonly CSharpCompilationOptions _compilationOptions;
private readonly ILogger _logger;
private readonly DependencyContext _dependencyContext;
private object _applicationReferencesLock = new object();
private bool _applicationReferencesInitialized;
private List<MetadataReference> _applicationReferences;
Expand All @@ -51,26 +51,12 @@ public DefaultRoslynCompilationService(
IOptions<RazorViewEngineOptions> optionsAccessor,
IRazorViewEngineFileProviderAccessor fileProviderAccessor,
ILoggerFactory loggerFactory)
: this(
GetDependencyContext(environment),
optionsAccessor.Value,
fileProviderAccessor,
loggerFactory)
{
}

// Internal for unit testing
internal DefaultRoslynCompilationService(
DependencyContext dependencyContext,
RazorViewEngineOptions viewEngineOptions,
IRazorViewEngineFileProviderAccessor fileProviderAccessor,
ILoggerFactory loggerFactory)
{
_dependencyContext = dependencyContext;
_hostingEnvironment = environment;
_fileProvider = fileProviderAccessor.FileProvider;
_compilationCallback = viewEngineOptions.CompilationCallback;
_parseOptions = viewEngineOptions.ParseOptions;
_compilationOptions = viewEngineOptions.CompilationOptions;
_compilationCallback = optionsAccessor.Value.CompilationCallback;
_parseOptions = optionsAccessor.Value.ParseOptions;
_compilationOptions = optionsAccessor.Value.CompilationOptions;
_logger = loggerFactory.CreateLogger<DefaultRoslynCompilationService>();
}

Expand Down Expand Up @@ -165,6 +151,22 @@ public CompilationResult Compile(RelativeFileInfo fileInfo, string compilationCo
}
}

/// <summary>
/// Gets the <see cref="DependencyContext"/>.
/// </summary>
/// <param name="hostingEnvironment">The <see cref="IHostingEnvironment"/>.</param>
/// <returns>The <see cref="DependencyContext"/>.</returns>
protected virtual DependencyContext GetDependencyContext(IHostingEnvironment hostingEnvironment)
{
if (hostingEnvironment.ApplicationName != null)
{
var applicationAssembly = Assembly.Load(new AssemblyName(hostingEnvironment.ApplicationName));
return DependencyContext.Load(applicationAssembly);
}

return null;
}

private Assembly LoadStream(MemoryStream assemblyStream, MemoryStream pdbStream)
{
#if NET451
Expand Down Expand Up @@ -241,16 +243,17 @@ private static string GetFilePath(string relativePath, Diagnostic diagnostic)
private List<MetadataReference> GetApplicationReferences()
{
var metadataReferences = new List<MetadataReference>();
if (_dependencyContext == null)
var dependencyContext = GetDependencyContext(_hostingEnvironment);
if (dependencyContext == null)
{
// Avoid null ref if the entry point does not have DependencyContext specified.
return metadataReferences;
}

var libraryPaths = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
for (var i = 0; i < _dependencyContext.CompileLibraries.Count; i++)
for (var i = 0; i < dependencyContext.CompileLibraries.Count; i++)
{
var library = _dependencyContext.CompileLibraries[i];
var library = dependencyContext.CompileLibraries[i];
IEnumerable<string> referencePaths;
try
{
Expand Down Expand Up @@ -322,16 +325,5 @@ private static DiagnosticMessage GetDiagnosticMessage(Diagnostic diagnostic)
mappedLineSpan.EndLinePosition.Line + 1,
mappedLineSpan.EndLinePosition.Character + 1);
}

private static DependencyContext GetDependencyContext(IHostingEnvironment environment)
{
if (environment.ApplicationName != null)
{
var applicationAssembly = Assembly.Load(new AssemblyName(environment.ApplicationName));
return DependencyContext.Load(applicationAssembly);
}

return null;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@
using System;
using System.Linq;
using System.Reflection;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Razor.Compilation;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Text;
using Microsoft.Extensions.DependencyModel;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Logging.Testing;
using Microsoft.Extensions.Options;
using Moq;
using Xunit;

Expand All @@ -24,11 +26,10 @@ public void Compile_ReturnsCompilationResult()
var content = @"
public class MyTestType {}";

var compilationService = new DefaultRoslynCompilationService(
var compilationService = new TestableRoslynCompilationService(
GetDependencyContext(),
GetOptions(),
GetFileProviderAccessor(),
NullLoggerFactory.Instance);
GetFileProviderAccessor());
var relativeFileInfo = new RelativeFileInfo(
new TestFileInfo { PhysicalPath = "SomePath" },
"some-relative-path");
Expand All @@ -52,11 +53,10 @@ public void Compile_ReturnsCompilationFailureWithPathsFromLinePragmas()
var fileProvider = new TestFileProvider();
var fileInfo = fileProvider.AddFile(viewPath, fileContent);

var compilationService = new DefaultRoslynCompilationService(
var compilationService = new TestableRoslynCompilationService(
GetDependencyContext(),
GetOptions(),
GetFileProviderAccessor(fileProvider),
NullLoggerFactory.Instance);
GetFileProviderAccessor(fileProvider));
var relativeFileInfo = new RelativeFileInfo(fileInfo, "some-relative-path");

// Act
Expand All @@ -77,11 +77,10 @@ public void Compile_ReturnsGeneratedCodePath_IfLinePragmaIsNotAvailable()
var fileContent = "file content";
var content = @"this should fail";

var compilationService = new DefaultRoslynCompilationService(
var compilationService = new TestableRoslynCompilationService(
GetDependencyContext(),
GetOptions(),
GetFileProviderAccessor(),
NullLoggerFactory.Instance);
GetFileProviderAccessor());
var relativeFileInfo = new RelativeFileInfo(
new TestFileInfo { Content = fileContent },
"some-relative-path");
Expand Down Expand Up @@ -113,11 +112,10 @@ public void Compile_DoesNotThrow_IfFileCannotBeRead()
var fileProvider = new TestFileProvider();
fileProvider.AddFile(path, mockFileInfo.Object);

var compilationService = new DefaultRoslynCompilationService(
var compilationService = new TestableRoslynCompilationService(
GetDependencyContext(),
GetOptions(),
GetFileProviderAccessor(),
NullLoggerFactory.Instance);
GetFileProviderAccessor());
var relativeFileInfo = new RelativeFileInfo(mockFileInfo.Object, path);

// Act
Expand Down Expand Up @@ -146,11 +144,10 @@ public class MyNonCustomDefinedClass {}
var options = GetOptions();
options.ParseOptions = options.ParseOptions.WithPreprocessorSymbols("MY_CUSTOM_DEFINE");

var compilationService = new DefaultRoslynCompilationService(
var compilationService = new TestableRoslynCompilationService(
GetDependencyContext(),
options,
GetFileProviderAccessor(),
NullLoggerFactory.Instance);
GetFileProviderAccessor());
var relativeFileInfo = new RelativeFileInfo(
new TestFileInfo { PhysicalPath = "SomePath" },
"some-relative-path");
Expand All @@ -174,11 +171,10 @@ public void GetCompilationFailedResult_ReturnsCompilationResult_WithGroupedMessa
var options = new RazorViewEngineOptions();
options.FileProviders.Add(fileProvider);

var compilationService = new DefaultRoslynCompilationService(
var compilationService = new TestableRoslynCompilationService(
GetDependencyContext(),
options,
GetFileProviderAccessor(fileProvider),
NullLoggerFactory.Instance);
GetFileProviderAccessor(fileProvider));

var assemblyName = "random-assembly-name";

Expand Down Expand Up @@ -261,11 +257,10 @@ public void Compile_RunsCallback()
var content = "public class MyTestType {}";
RoslynCompilationContext usedCompilation = null;

var compilationService = new DefaultRoslynCompilationService(
var compilationService = new TestableRoslynCompilationService(
GetDependencyContext(),
GetOptions(callback: c => usedCompilation = c),
GetFileProviderAccessor(),
NullLoggerFactory.Instance);
GetFileProviderAccessor());

var relativeFileInfo = new RelativeFileInfo(
new TestFileInfo { PhysicalPath = "SomePath" },
Expand All @@ -283,11 +278,10 @@ public void Compile_ThrowsIfDependencyContextIsNullAndTheApplicationFailsToCompi
{
// Arrange
var content = "public class MyTestType {}";
var compilationService = new DefaultRoslynCompilationService(
var compilationService = new TestableRoslynCompilationService(
dependencyContext: null,
viewEngineOptions: GetOptions(),
fileProviderAccessor: GetFileProviderAccessor(),
loggerFactory: NullLoggerFactory.Instance);
fileProviderAccessor: GetFileProviderAccessor());

var relativeFileInfo = new RelativeFileInfo(
new TestFileInfo { PhysicalPath = "SomePath" },
Expand All @@ -313,11 +307,10 @@ public void Compile_ThrowsIfDependencyContextReturnsNoReferencesAndTheApplicatio
new CompilationLibrary[0],
new RuntimeLibrary[0],
Enumerable.Empty<RuntimeFallbacks>());
var compilationService = new DefaultRoslynCompilationService(
var compilationService = new TestableRoslynCompilationService(
dependencyContext: dependencyContext,
viewEngineOptions: GetOptions(),
fileProviderAccessor: GetFileProviderAccessor(),
loggerFactory: NullLoggerFactory.Instance);
fileProviderAccessor: GetFileProviderAccessor());

var relativeFileInfo = new RelativeFileInfo(
new TestFileInfo { PhysicalPath = "SomePath" },
Expand All @@ -341,11 +334,10 @@ public void Compile_DoesNotThrowIfReferencesWereClearedInCallback()
context.Compilation = context.Compilation.RemoveAllReferences();
});
var content = "public class MyTestType {}";
var compilationService = new DefaultRoslynCompilationService(
var compilationService = new TestableRoslynCompilationService(
dependencyContext: GetDependencyContext(),
viewEngineOptions: options,
fileProviderAccessor: GetFileProviderAccessor(),
loggerFactory: NullLoggerFactory.Instance);
fileProviderAccessor: GetFileProviderAccessor());

var relativeFileInfo = new RelativeFileInfo(
new TestFileInfo { PhysicalPath = "SomePath" },
Expand All @@ -371,11 +363,10 @@ public void Compile_SucceedsIfReferencesAreAddedInCallback()
.AddReferences(MetadataReference.CreateFromFile(assemblyLocation));
});
var content = "public class MyTestType {}";
var compilationService = new DefaultRoslynCompilationService(
var compilationService = new TestableRoslynCompilationService(
dependencyContext: null,
viewEngineOptions: options,
fileProviderAccessor: GetFileProviderAccessor(),
loggerFactory: NullLoggerFactory.Instance);
fileProviderAccessor: GetFileProviderAccessor());

var relativeFileInfo = new RelativeFileInfo(
new TestFileInfo { PhysicalPath = "SomePath" },
Expand Down Expand Up @@ -422,5 +413,33 @@ private DependencyContext GetDependencyContext()
var assembly = typeof(DefaultRoslynCompilationServiceTest).GetTypeInfo().Assembly;
return DependencyContext.Load(assembly);
}

private class TestableRoslynCompilationService : DefaultRoslynCompilationService
{
private readonly DependencyContext _dependencyContext;

public TestableRoslynCompilationService(
DependencyContext dependencyContext,
RazorViewEngineOptions viewEngineOptions,
IRazorViewEngineFileProviderAccessor fileProviderAccessor)
: base(
Mock.Of<IHostingEnvironment>(),
GetAccessor(viewEngineOptions),
fileProviderAccessor,
NullLoggerFactory.Instance)
{
_dependencyContext = dependencyContext;
}

private static IOptions<RazorViewEngineOptions> GetAccessor(RazorViewEngineOptions options)
{
var optionsAccessor = new Mock<IOptions<RazorViewEngineOptions>>();
optionsAccessor.SetupGet(a => a.Value).Returns(options);
return optionsAccessor.Object;
}

protected override DependencyContext GetDependencyContext(IHostingEnvironment hostingEnvironment)
=> _dependencyContext;
}
}
}

0 comments on commit 0ff2f87

Please sign in to comment.