//----------------------------------------------------------------------- // // Created Sept. 2009 by Nicholas Armstrong. Available online at http://nicholasarmstrong.com // // // Dynamically compiles C# code into an assembly and executes it. // //----------------------------------------------------------------------- namespace NicholasArmstrong.Samples.ECE150.Loops { using System; using System.CodeDom.Compiler; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Text; using System.Text.RegularExpressions; using System.Windows; using System.Windows.Controls; using Microsoft.CSharp; #region Delegates /// /// Delegate for the 'Highlight' function. /// /// The position of the box to highlight. /// The colour to highlight the box. public delegate void HighlightDelegate(int position, string color); /// /// Delegate for the 'HighlightBox' function. /// /// The box to highlight. /// The colour to highlight the box. public delegate void HighlightBoxDelegate(Box box, string color); /// /// Delegate for the 'Wait' function. /// /// The duration to wait, in milliseconds. public delegate void WaitDelegate(int millisecondsTimeout); #endregion /// /// Dynamically compiles C# code into an assembly and executes it. /// public class DynamicEval { #region Fields /// /// Regular expression to change the 1-argument Highlight function into the two-argument version. /// private static readonly Regex highlightOverloadRegex = new Regex("Highlight[(]([^,(]+)[)]"); /// /// Regular expression to change the 1-argument HighlightBox function into the two-argument version. /// private static readonly Regex highlightBoxOverloadRegex = new Regex("HighlightBox[(]([^,(]+)[)]"); /// /// The 'Highlight()' function. /// private HighlightDelegate highlightDelegate; /// /// The 'HighlightBox()' function. /// private HighlightBoxDelegate highlightBoxDelegate; /// /// The 'Wait()' function. /// private WaitDelegate waitDelegate; /// /// The last compiled assembly. /// private Assembly compiledAssembly; /// /// Parameters needed for compilation. /// private CompilerParameters cp; /// /// The C# compiler service. /// private CSharpCodeProvider csc; /// /// The starting portions of the program that actually gets compiled. /// private string programStartText; /// /// The ending portions of the program that actually gets compiled. /// private string programEndText; #endregion #region Constructor /// /// Initializes a new instance of the DynamicEval class. /// /// An implementation of the 'Highlight' function. /// An implementation of the 'HighlightBox' function. /// An implementation of the 'Wait' function. public DynamicEval(HighlightDelegate highlightDelegate, HighlightBoxDelegate highlightBoxDelegate, WaitDelegate waitDelegate) { this.highlightDelegate = highlightDelegate; this.highlightBoxDelegate = highlightBoxDelegate; this.waitDelegate = waitDelegate; this.Initialize(); } #endregion #region Methods /// /// Initializes the dynamic evaluation class. /// public void Initialize() { // Initialize Compiler this.cp = new CompilerParameters(); this.cp.GenerateExecutable = false; this.cp.GenerateInMemory = true; this.cp.ReferencedAssemblies.Add("ECE150.Loops.dll"); this.cp.ReferencedAssemblies.Add("mscorlib.dll"); this.cp.ReferencedAssemblies.Add("System.dll"); this.cp.ReferencedAssemblies.Add(typeof(Queryable).Assembly.Location); // Reference LINQ libraries in System.Core this.cp.ReferencedAssemblies.Add(typeof(Control).Assembly.Location); // Reference WPF's PresentationFramework.dll this.cp.ReferencedAssemblies.Add(typeof(UIElement).Assembly.Location); // Reference WPF's PresentationCore.dll this.cp.ReferencedAssemblies.Add(typeof(DependencyProperty).Assembly.Location); // Reference WPF's WindowsBase.dll this.csc = new CSharpCodeProvider(new Dictionary() { { "CompilerVersion", "v3.5" } }); // Allow LINQ queries to be compiled // Initialize static program text StringBuilder s = new StringBuilder(); s.AppendLine("using System;"); s.AppendLine("using System.Collections.Generic;"); s.AppendLine("using System.Linq;"); s.AppendLine("namespace NicholasArmstrong.Samples.ECE150.Loops"); s.AppendLine("{"); s.AppendLine("public static class CompiledEval"); s.AppendLine("{"); s.AppendLine("public static void HighlightBoxes(HighlightDelegate Highlight, HighlightBoxDelegate HighlightBox, WaitDelegate Wait, int totalBoxes, int boxesPerColumn, int boxesPerRow, IEnumerable boxes)"); s.AppendLine("{"); s.AppendLine("int totalColumns = boxesPerRow;"); s.AppendLine("int totalRows = boxesPerColumn;"); this.programStartText = s.ToString(); this.programEndText = "}}}"; } /// /// Compiles a snippet of code into an assembly. /// /// The function text to compile. /// Any error messages produced during compilation. /// A value indicating whether compilation was successful. public bool CompileSnippet(string functionText, out string errorMessages) { // Preprocess program functionText = highlightOverloadRegex.Replace(functionText, "Highlight($1, String.Empty)"); functionText = highlightBoxOverloadRegex.Replace(functionText, "HighlightBox($1, String.Empty)"); // Construct program string programText = this.programStartText + functionText + this.programEndText; // Compile program CompilerResults results = this.csc.CompileAssemblyFromSource(this.cp, programText); // Collect error messages errorMessages = String.Empty; foreach (CompilerError error in results.Errors) { // Append error messages, make a rough attempt to hide some of the fancy work we're doing in behind the scenes to compile errorMessages += error.ErrorText.Replace("HighlightDelegate", "Highlight").Replace("WaitDelegate", "Wait").Replace("Delegate", "Method").Replace("delegate", "method") + "\n"; } if (errorMessages.Length > 0) { // Trim off the last newline errorMessages = errorMessages.Substring(0, errorMessages.Length - 1); } this.compiledAssembly = results.Errors.HasErrors ? null : results.CompiledAssembly; return !results.Errors.HasErrors; } /// /// Executes the compiled snippet of code with the provided parameters. /// /// The total number of boxes in the grid. /// The number of boxes in a grid column. /// The number of boxes in a grid row. /// The collection of boxes themselves (for foreach and LINQ support). public void ExecuteSnippet(int totalBoxes, int boxesPerColumn, int boxesPerRow, IEnumerable boxes) { if (this.compiledAssembly == null) { return; } Type target = this.compiledAssembly.GetTypes()[0]; MethodInfo method = target.GetMethod("HighlightBoxes"); object[] parameters = new object[] { this.highlightDelegate, this.highlightBoxDelegate, this.waitDelegate, totalBoxes, boxesPerColumn, boxesPerRow, boxes }; method.Invoke(null, parameters); } #endregion } }