Compile string as code

21

How can I compile a string inside C #? Example: Console.WriteLine(\"Hello World\"); .

As if it were a eval of JavaScript?

I had a project to load a code into a text file or something else.

    
asked by anonymous 10.05.2014 / 02:10

2 answers

23

Well, you can already generate arbitrary code and run it at runtime. But not in a simple way, not in this fluid way. You already had reflection or introspection as some would prefer to call in the specific case. See at the end.

.NET Compiler Platform

Have you heard of the .NET Compiler Platform (old Roslyn)?

It is the C # 6.0 compiler (coming along with Visual Studio 2015 and later).

The new compiler is written in C # and has some advantages:

  • is much easier to maintain than the previous version written in C ++;
  • is open source (Apache license, pretty permissive, do pretty much anything you want with it);
  • allows community participation in its development;
  • Enable language enhancements faster and cheaper;
  • in majority cases it is faster on modern computers;
  • has an architecture called Compiler As A Service ;

The latter is what interests you. There is a very complete API that makes it easy to create additional language tools in a very simple way and guaranteed to handle everything with the correct and most current language, because it is the compiler that is providing all the code analyzes. The Visual Studio tools themselves are fully based on the .NET Compoiler Platform. One of the tools is to convert code from C # to VB.Net and vice versa in a practical, precise and simple way.

Another tool being developed with it is a REPL which is a very practical way of testing codes, learning language features. It greatly shortens prototype time. REPL is a kind of interpreter.

Basically the same API that allows you to create REPL solves your problem.

Engine Script

using Microsoft.CodeAnalysis.Scripting;
using Microsoft.CodeAnalysis.Scripting.CSharp;

namespace RoslynScriptingDemo {
    class Program {
        static void Main(string[] args) {
            var engine = new ScriptEngine();
            engine.Execute(@"System.Console.WriteLine(""Hello Roslyn"");");
        }
    }
}

This API allows you to create a engine script that runs arbitrary code.

Another slightly more complex example:

using System;
using Microsoft.CodeAnalysis.Scripting;
using Microsoft.CodeAnalysis.Scripting.CSharp;

namespace Test.ConsoleApp {
    public class Program {
        static void Main(string[] args) {
            var engine = new ScriptEngine();

            var session = engine.CreateSession();

            session.AddReference("System");
            session.AddReference("System.Linq");

            session.Execute(@"using System;
                              var arr = new[] {1, 2, 6};
                              foreach (var i in arr) {
                                  if(i > 1) { Console.WriteLine(i); }
                              }"
                           );

            session.Execute(@"using System;
                              using System.Linq;
                              var arr = new[] {1, 2, 6};
                              var arrResult = arr.Where(x => x > 1);
                              foreach (var i in arrResult) {
                                  Console.WriteLine(i);
                              }"
                           );

            Console.ReadLine();
        }
    }
}

I imagine it should not be difficult for you to load the text of the file into a string to execute. But in the last CTP I saw, I had a method to execute directly from a file instead of a string . But it does not make much difference, it is more a convenience. The API is very complete and very easy. It was something like this:

scriptEngine.ExecuteFile("script.cs");

Security

Of course arbitrary codes that can be executed are dangerous. Where do these scripts come from? Who can make them available? Who can manipulate them?

Of course, this scripts system has fewer risks than eval . of JavaScript. It's harder for someone to inject malicious code into a system developed by you than on a web page. But if you do not take proper care, it happens the same way.

Anyway, when you use this type of resource you have to understand that there are risks and they need to be minimized. Extensive checks before executing a file from outside need to be performed. But the most important thing is to be aware that there is an open door to malicious executions.

C # already runs on a kind of sandbox and gives certain assurances to avoid certain security issues. But arbitrary codes can always do more than you expect.

There is another non-security aspect, which is handling exceptions generated in script . This needs to be understood. But it's a subject that goes beyond your question.

Alternative

You can now use a limited and more complicated form of this through CodeDOM .

I will not go into detail since I do not know the subject very well, but basically it is composed of a series of classes that allow you to assemble the source code from a source code. But I warn you that the .Net developers themselves understand that it is very complex and that the .NET Compiler Platform is going to trivialize the use of arbitrary code.

There is still a way to issue CIL directly > but not is what you need.

I've placed it on GitHub for future reference.

    
10.05.2014 / 02:28
8

One possible solution is to use CodeDOM .

An example of how to use:

    public static void Main(string[] args)
    {
        var csc = new CSharpCodeProvider();
        var parameters = new CompilerParameters(new[] {"mscorlib.dll", "System.Core.dll"}, "teste.exe", true)
            {
                GenerateExecutable = true
            };
        CompilerResults compiledAssembly = csc.CompileAssemblyFromSource(parameters,
        @"
        using System;
        class Program {
          public static void Main() {
                Console.WriteLine(""Hello world."");
          }
        }");

        var errors = compiledAssembly.Errors
                                     .Cast<CompilerError>()
                                     .ToList();

        if (errors.Any())
        {
            errors.ForEach(Console.WriteLine);
            return;
        }

        Module module = compiledAssembly.CompiledAssembly.GetModules()[0];

        Type mt = null;
        if (module != null)
            mt = module.GetType("Program");

        MethodInfo methInfo = null;
        if (mt != null)
            methInfo = mt.GetMethod("Main");

        if (methInfo != null)
            Console.WriteLine(methInfo.Invoke(null, null));
    }

The code is compiled by the class CSharpCodeProvider and can later be used by reflection on the generated assembly .

You can find another example here .

    
10.05.2014 / 02:53