using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.Net.Runtime; namespace Microsoft.AspNet.Mvc.Razor.Compilation { public class RoslynCompilationService : ICompilationService { private readonly ILibraryManager _libraryManager; private readonly IApplicationEnvironment _environment; private readonly IAssemblyLoaderEngine _loader; public RoslynCompilationService(IApplicationEnvironment environment, IAssemblyLoaderEngine loaderEngine, ILibraryManager libraryManager) { _environment = environment; _loader = loaderEngine; _libraryManager = libraryManager; } public Task Compile(string content) { var syntaxTrees = new[] { CSharpSyntaxTree.ParseText(content) }; var targetFramework = _environment.TargetFramework; var references = GetApplicationReferences(); var assemblyName = Path.GetRandomFileName(); var compilation = CSharpCompilation.Create(assemblyName, options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary), syntaxTrees: syntaxTrees, references: references); using (var ms = new MemoryStream()) { using (var pdb = new MemoryStream()) { var result = compilation.Emit(ms, pdbStream: pdb); if (!result.Success) { var formatter = new DiagnosticFormatter(); var messages = result.Diagnostics.Where(IsError).Select(d => GetCompilationMessage(formatter, d)).ToList(); return Task.FromResult(CompilationResult.Failed(content, messages)); } var type = _loader.LoadBytes(ms.ToArray(), pdb.ToArray()) .GetExportedTypes() .First(); return Task.FromResult(CompilationResult.Successful(String.Empty, type)); } } } private List GetApplicationReferences() { var references = new List(); var export = _libraryManager.GetLibraryExport(_environment.ApplicationName); foreach (var metadataReference in export.MetadataReferences) { var fileMetadataReference = metadataReference as IMetadataFileReference; if (fileMetadataReference != null) { references.Add(CreateMetadataFileReference(fileMetadataReference.Path)); } else { var roslynReference = metadataReference as IRoslynMetadataReference; if (roslynReference != null) { references.Add(roslynReference.MetadataReference); } } } return references; } private MetadataReference CreateMetadataFileReference(string path) { #if NET45 return new MetadataFileReference(path); #else // TODO: What about access to the file system? We need to be able to // read files from anywhere on disk, not just under the web root using (var stream = File.OpenRead(path)) { return new MetadataImageReference(stream); } #endif } private CompilationMessage GetCompilationMessage(DiagnosticFormatter formatter, Diagnostic diagnostic) { return new CompilationMessage(formatter.Format(diagnostic)); } private bool IsError(Diagnostic diagnostic) { return diagnostic.IsWarningAsError || diagnostic.Severity == DiagnosticSeverity.Error; } } }