Compiling and safely executing code on the fly

    Problem


    In your application, you need to execute code that is not known at the stage of compiling the application. It can be a variety of plugins, extensions, calculations, etc.

    Decision


    .NET allows you to compile and execute code. Moreover, you can safely execute code by narrowing the range of resources that the executable code can use.



    Implementation


    For compilation, we need a CodeDomProvider, with which you can instantiate a compiler instance for any .NET language. Let's move on to the code.

    1. private string EvalCode (string typeName, string methodName, string sourceCode)
    2.         {
    3.             string output = ":)";
    4.             var compiler = CodeDomProvider.CreateProvider ("CSharp");
    5.             var parameters = new CompilerParameters
    6.             {
    7.                 CompilerOptions = "/ t: library",
    8.                 GenerateInMemory = true,
    9.                 IncludeDebugInformation = true
    10.             };
    11.             var results = compiler.CompileAssemblyFromSource (parameters, sourceCode);
    12.  
    13.             if (! results.Errors.HasErrors)
    14.             {
    15.                 var assembly = results.CompiledAssembly;
    16.                 var evaluatorType = assembly.GetType (typeName);
    17.                 var evaluator = Activator.CreateInstance (evaluatorType);
    18.  
    19.                 output = (string) InvokeMethod (evaluatorType, methodName, evaluator, new object [] {output});
    20.                 return output;
    21.             }
    22.  
    23.             output = "\ r \ nHouston, we have a problem at compile time!";
    24.             return results.Errors.Cast() .Aggregate (output, (current, ce) => current + string.Format ("\ r \ nline {0}: {1}", ce.Line, ce.ErrorText));
    25.         }
    26.  
    27.         [FileIOPermission (SecurityAction.Deny, Unrestricted = true)]
    28.         private object InvokeMethod (Type evaluatorType, string methodName, object evaluator, object [] methodParams)
    29.         {
    30.             return evaluatorType.InvokeMember (methodName, System.Reflection.BindingFlags.InvokeMethod, null, evaluator, methodParams);
    31.         }


    As you can see, there is nothing supernatural. We create the compiler, pass the code into it, we get the DLL. Using reflection, we start the desired method.

    Code in action:


    Line 27 is interesting. Using it, you can limit the rights that a code has (not only dynamic). .NET has a Code Access Security mechanism that allows you to control the security of the code you execute.

    Security error when trying to write to disk:


    Despite the fact that the code execution looks “harmless”, it is worth remembering that this is a very resource-intensive process. For example, if you execute code in the current domain of the application (as it was done in the demo), during prolonged operation of the application the memory may simply end (.NET cannot unload assemblies from the domain).

    Demo:
    Dyndllfunwf.zip

    Also popular now: