/// <summary>Compile a c# script.</summary> /// <param name="code">The c# code to compile.</param> /// <param name="model">The model owning the script.</param> /// <param name="referencedAssemblies">Optional referenced assemblies.</param> /// <returns>Compile errors or null if no errors.</returns> public Results Compile(string code, IModel model, IEnumerable <string> referencedAssemblies = null) { string errors = null; if (code != null) { // See if we have compiled the code already. If so then no need to compile again. var compilation = previousCompilations.Find(c => c.Code == code); bool newlyCompiled; if (compilation == null || compilation.Code != code) { newlyCompiled = true; var assemblies = GetReferenceAssemblies(referencedAssemblies, model.Name); // We haven't compiled the code so do it now. var result = CompileTextToAssembly(code, assemblies); if (result.Errors.Count > 0) { // Errors were found. Add then to the return error string. errors = null; foreach (CompilerError err in result.Errors) { errors += $"Line {err.Line}: {err.ErrorText}{Environment.NewLine}"; } // Because we have errors, remove the previous compilation if there is one. if (compilation != null) { previousCompilations.Remove(compilation); } compilation = null; } else { // No errors. // If we don't have a previous compilation, create one. if (compilation == null) { compilation = new PreviousCompilation() { ModelFullPath = model.FullPath }; previousCompilations.Add(compilation); } // Set the compilation properties. compilation.Code = code; compilation.CompiledAssembly = result.CompiledAssembly; } } else { newlyCompiled = false; } if (compilation != null) { // We have a compiled assembly so get the class name. var regEx = new Regex(@"class\s+(\w+)\s"); var match = regEx.Match(code); if (!match.Success) { throw new Exception($"Cannot find a class declaration in script:{Environment.NewLine}{code}"); } var className = match.Groups[1].Value; // Create an instance of the class and give it to the model. var instanceType = compilation.CompiledAssembly.GetTypes().ToList().Find(t => t.Name == className); return(new Results(compilation.CompiledAssembly, instanceType.FullName, newlyCompiled)); } else { return(new Results(errors)); } } return(null); }
/// <summary>Compile a c# script.</summary> /// <param name="code">The c# code to compile.</param> /// <param name="model">The model owning the script.</param> /// <param name="referencedAssemblies">Optional referenced assemblies.</param> /// <returns>Compile errors or null if no errors.</returns> public Results Compile(string code, IModel model, IEnumerable <MetadataReference> referencedAssemblies = null) { string errors = null; if (code != null) { // See if we have compiled the code already. If so then no need to compile again. PreviousCompilation compilation = previousCompilations.Find(c => c.Code == code); bool newlyCompiled; if (compilation == null || compilation.Code != code) { newlyCompiled = true; bool withDebug = System.Diagnostics.Debugger.IsAttached; IEnumerable <MetadataReference> assemblies = GetReferenceAssemblies(referencedAssemblies, model.Name); // We haven't compiled the code so do it now. string sourceName; Compilation compiled = CompileTextToAssembly(code, assemblies, out sourceName); List <EmbeddedText> embeddedTexts = null; if (withDebug) { System.Text.Encoding encoding = System.Text.Encoding.UTF8; byte[] buffer = encoding.GetBytes(code); SourceText sourceText = SourceText.From(buffer, buffer.Length, encoding, canBeEmbedded: true); embeddedTexts = new List <EmbeddedText> { EmbeddedText.FromSource(sourceName, sourceText), }; } MemoryStream ms = new MemoryStream(); MemoryStream pdbStream = new MemoryStream(); { EmitResult emitResult = compiled.Emit( peStream: ms, pdbStream: withDebug ? pdbStream : null, embeddedTexts: embeddedTexts ); if (!emitResult.Success) { // Errors were found. Add then to the return error string. errors = null; foreach (Diagnostic diag in emitResult.Diagnostics) { if (diag.Severity == DiagnosticSeverity.Error) { errors += $"{diag.ToString()}{Environment.NewLine}"; } } // Because we have errors, remove the previous compilation if there is one. if (compilation != null) { previousCompilations.Remove(compilation); } compilation = null; } else { // No errors. // If we don't have a previous compilation, create one. if (compilation == null) { compilation = new PreviousCompilation() { ModelFullPath = model.FullPath }; previousCompilations.Add(compilation); } // Write the assembly to disk ms.Seek(0, SeekOrigin.Begin); string fileName = Path.Combine(Path.GetTempPath(), compiled.AssemblyName + ".dll"); using (FileStream file = new FileStream(fileName, FileMode.Create, FileAccess.Write)) ms.WriteTo(file); // Set the compilation properties. ms.Seek(0, SeekOrigin.Begin); pdbStream.Seek(0, SeekOrigin.Begin); compilation.Code = code; compilation.Reference = compiled.ToMetadataReference(); compilation.CompiledAssembly = System.Runtime.Loader.AssemblyLoadContext.Default.LoadFromStream(ms, pdbStream); } } } else { newlyCompiled = false; } if (compilation != null) { // We have a compiled assembly so get the class name. var regEx = new Regex(@"class\s+(\w+)\s"); var match = regEx.Match(code); if (!match.Success) { throw new Exception($"Cannot find a class declaration in script:{Environment.NewLine}{code}"); } var className = match.Groups[1].Value; // Create an instance of the class and give it to the model. var instanceType = compilation.CompiledAssembly.GetTypes().ToList().Find(t => t.Name == className); return(new Results(compilation.CompiledAssembly, instanceType.FullName, newlyCompiled)); } else { return(new Results(errors)); } } return(null); }