Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mono Compiler as a Service (MCS)

I'd like to consume Mono's compiler as a service from my regular .NET 3.5 application.

I've downloaded the latest bits (2.6.7), created a simple console application in Visual Studio and referenced the Mono.CSharp dll.

Then, in my console app (straight out of a sample online):

    Evaluator.Run("using System; using System.Linq;");
    bool ress;
    object res;
    Evaluator.Evaluate(
         "from x in System.IO.Directory.GetFiles (\"C:\\\") select x;",
         out res, out ress);

    foreach (var v in (IEnumerable)res)
    {
        Console.Write(v);
        Console.Write(' ');
    }

This throws an exception at Evaluator.Run (the first line):

Illegal enum value: 2049.
Parameter name: access

This is because the dll was compiled using Mono.exe, not csc.exe, I believe.

I've tried downloading the Mono.CSharp dll directly from http://tirania.org/blog/archive/2010/Apr-27.html in the demo-repl.zip file...and that does not throw an exception...However the out parameter (res) after calling Evaluator.Evaluate is null...so I'm not sure what's going wrong. No exception is thrown...

So, I'd like to figure out why the dll I downloaded from the demo-repl.zip returns null.

EDIT: I figured out why it returns null. It seems like for some reason the compiler isn't picking up the System.Linq namespace...though I can't tell why...If I just Evaluate "System.IO.Directory.GetFiles (\"C:\\")", it works fine.

UPDATE: It definitely seems like there's something wrong with the Mono compiler picking up referenced System assemblies. If I directly copy the sample of their csharp console tool:

csharp> var list = new int [] {1,2,3};
csharp> var b = from x in list
   >    where x > 1
   >    select x;
csharp> b;

I get the exception:

{interactive}(1,25): error CS1935: An implementation of `Select' query expressio
n pattern could not be found. Are you missing `System.Linq' using directive or `
System.Core.dll' assembly reference?

Also, in order for the MCS to actually be a feasible solution, I'll need to modify the compiler so that it emits to one single dynamic assembly, instead of emitting one assembly per evaluate call (otherwise it presents a major memory leak, which I've dealt with before in the form of the CSharpCodeProvider). Does anyone have an idea of how difficult this will be or can anyone point me in the right direction here?

Thanks.

like image 477
Jeff Avatar asked Aug 04 '10 15:08

Jeff


People also ask

What is MCS in mono?

mcs is the Mono C# compiler, an implementation of the ECMA-334 language specification. You can pass one or more options to drive the compiler, and a set of source files. Extra options or arguments can be provided in a response file. Response files are referenced by prepending the @ symbol to the response file name.

What is the compiler for C#?

Visual Studio - Windows only Visual Studio is the compiler and IDE created by Microsoft for Windows computers. Visual Studio has several versions. The professional version can compile C# code, as well as a number of other languages.

Is Mono a compiler?

The Mono C# compiler is considered feature complete for C# 1.0, C# 2.0, C# 3.0, C# 4.0, C# 5.0 and C# 6.0 (ECMA) and it has partial support for C# 7. Historically, various version of same compiler existed.


2 Answers

Ok, I think I have some answers.

To resolve the assembly load problem, I can either place a call to Assembly.LoadWithPartialName inside Mono.CSharp.Driver.LoadAssembly, or do the following in my application

        AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);

        private static bool isResolving;
        static System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
        {
            if (!isResolving)
            {
                isResolving = true;
                var a = Assembly.LoadWithPartialName(args.Name);
                isResolving = false;
                return a;
            }
            return null;
        }

To make Mono reuse the same dynamic assembly for each Evaluate/Compile call, all I had to change is the following (although there are probably complexities I'm missing here).....

Inside Mono.CSharp.Evaluator, I added the property:

/// <summary>
/// Gets or sets a value indicating whether to auto reset when evaluations are performed and create a new assembly.
/// </summary>
/// <value><c>true</c> if [auto reset]; otherwise, <c>false</c>.</value>
public static bool AutoReset { get; set; }

Then...make sure Reset is called at least once in Init:

    static void Init ()
    {
        Init (new string [0]);
        Reset();
    }

And finally, in ParseString, simply don't reset unless AutoReset is true...

        static CSharpParser ParseString (ParseMode mode, string input, out bool partial_input)
        {
.
.
.
            if (AutoReset) Reset ();
like image 65
Jeff Avatar answered Oct 03 '22 03:10

Jeff


According to Miguel's blog page you linked, you have to add a reference to System.Core in order to use LINQ on .Net.

csharp> using System.Linq;
csharp> from x in "Foo" select x;
like image 26
jpobst Avatar answered Oct 03 '22 04:10

jpobst