private static CompiledAssembly EmitCompiledTemplate(string newAssemblyName, CSharpCompilation compilation) { using (MemoryStream dllMemoryStream = new MemoryStream()) using (MemoryStream pdbMemoryStream = new MemoryStream()) { EmitResult result = compilation.Emit(dllMemoryStream, pdbMemoryStream); if (result.Success) { dllMemoryStream.Seek(0, SeekOrigin.Begin); pdbMemoryStream.Seek(0, SeekOrigin.Begin); CompiledAssembly compiledTemplate = new CompiledAssembly(newAssemblyName, dllMemoryStream.ToArray(), pdbMemoryStream.ToArray(), false, null); return(compiledTemplate); } else { List <string> errorMessages = result.Diagnostics .Where(d => d.IsWarningAsError || d.Severity == DiagnosticSeverity.Error) .Select(d => d.ToString()) .ToList(); CompiledAssembly compiledTemplate = new CompiledAssembly(newAssemblyName, new byte[0], new byte[0], true, errorMessages); return(compiledTemplate); } } }
public static CompiledAssembly Compile(string sourceText, string sourceFilename, string assemblyName, IEnumerable <string> additionalReferences = null) { try { bool hasErrors; List <string> errorMessages; SyntaxTree syntaxTree = ParseSourceCode(sourceText, sourceFilename); errorMessages = syntaxTree.GetDiagnostics() .Where(d => d.IsWarningAsError || d.Severity == DiagnosticSeverity.Error) .Select(d => d.ToString()) .ToList(); if (errorMessages.Any()) { return(new CompiledAssembly(assemblyName, new byte[0], new byte[0], true, errorMessages)); } MetadataReference[] references = CollectAssemblyReferences(additionalReferences, out hasErrors, out errorMessages); if (hasErrors) { return(new CompiledAssembly(assemblyName, new byte[0], new byte[0], true, errorMessages)); } CSharpCompilation compilation = SetupCompiler(assemblyName, syntaxTree, references); CompiledAssembly compiledTemplate = EmitCompiledTemplate(assemblyName, compilation); return(compiledTemplate); } catch (Exception e) { return(new CompiledAssembly(assemblyName, new byte[0], new byte[0], true, new[] { "Internal compiler error: " + e.Message })); } }
public CompiledAssembly LoadOrCompile(Options options, string sourceText, string sourceFilename, IEnumerable <string> additionalReferences = null) { CompiledAssembly assembly; options.Log("Generating final source code form."); // Generate the final compiled text itself. string finalSourceText = GenerateFinalSourceText(sourceText, sourceFilename, additionalReferences); // We use a content-addressible cache: The hash of the compiled // text is the name of the cache file. We truncate the hash to its first // 10 bytes (80 bits), since we don't need cryptographic security: We // just need a low probability of an accidental collision, and 80 bits // is plenty for that. string hashName = finalSourceText.SHA256().ToHexChars(false).Substring(0, 20); options.Log("Assembly name: {0}.dll", hashName); // If we already have it in memory, just use it. if (_templates.TryGetValue(hashName, out assembly)) { options.Log("Using in-memory cached assembly."); return(assembly); } options.Log("Searching for assembly in cache folder \"{0}\".", _folder); // Choose suitable output pathnames. string dllPath = Path.Combine(_folder, hashName + ".dll"); string pdbPath = Path.Combine(_folder, hashName + ".pdb"); // Try to load the cached DLL and PDB files. If they exist, // we'll use them: They have the right hash, so they must be // the right data. byte[] cachedDll, cachedPdb; // First, try to get the DLL. It's okay if we can't. try { cachedDll = File.ReadAllBytes(dllPath); if (cachedDll.Length == 0) { cachedDll = null; } } catch (IOException) { cachedDll = null; } if (cachedDll != null) { // Maybe load the PDB, if it exists. If it doesn't, no big deal: // the user just doesn't get debugging information, but at least // we don't have to compile the source code again. try { cachedPdb = File.ReadAllBytes(pdbPath); if (cachedPdb.Length == 0) { cachedPdb = null; } } catch (IOException) { cachedPdb = null; } options.Log("Using on-disk cached assembly."); // Got at least the DLL, if not the PDB, so we're good to go. assembly = new CompiledAssembly(hashName, cachedDll, cachedPdb); // Keep a copy in memory so we don't have to do it again. _templates.Add(hashName, assembly); // And we're done. return(assembly); } options.Log("Compiling new assembly."); // Don't have it, so compile it for real. This can be slow (seconds, // not milliseconds or microseconds). assembly = CSharpCompiler.Compile(finalSourceText, sourceFilename, hashName, additionalReferences); // Don't bother caching it if we can't compile it. if (assembly.HasErrors) { options.Log("Compile failed with errors."); return(assembly); } options.Log("Writing new assembly to cache folder \"{0}\".", _folder); // Try to write the compiled assembly to disk. If we can't, // the cache is just slower across runs, but it's not really // an error. try { Directory.CreateDirectory(_folder); } catch (Exception e) { options.Log("Cannot ensure \"{0}\" exists: {1}", _folder, e.Message); } try { File.WriteAllBytes(dllPath, assembly.Dll); } catch (Exception e) { options.Log("Cannot write to \"{0}\": {1}", dllPath, e.Message); } try { File.WriteAllBytes(pdbPath, assembly.Pdb); } catch (Exception e) { options.Log("Cannot write to \"{0}\": {1}", pdbPath, e.Message); } return(assembly); }