Question

I'm trying to create simple .net compiler for educational purposes. After parsing, scanning and building AST I'm generating .net assembly using Reflection.Emit.ILGenerator.

Here is my sample code for assembly generation:

static void Main(string[] args)
{
    string moduleName = "Test.exe";

    AssemblyName assemblyName = new AssemblyName(Path.GetFileNameWithoutExtension(moduleName));
    AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Save);
    ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(moduleName);
    TypeBuilder typeBuilder = moduleBuilder.DefineType("Program", TypeAttributes.Public);
    MethodBuilder mainMethod = typeBuilder.DefineMethod(
        "Main", MethodAttributes.Public | MethodAttributes.Static, typeof(void), System.Type.EmptyTypes);


    ILGenerator il = mainMethod.GetILGenerator();

    il.Emit(OpCodes.Ldstr, "Test!");
    il.Emit(OpCodes.Call, typeof(System.Console).GetMethod("WriteLine", new System.Type[] { typeof(string) }));
    il.Emit(OpCodes.Ret);

    typeBuilder.CreateType();
    moduleBuilder.CreateGlobalFunctions();
    assemblyBuilder.SetEntryPoint(mainMethod);
    assemblyBuilder.Save(moduleName);
}

Everything works fine, this code generates executable with following class:

using System;

public class Program
{
    public Program()
    {
    }

    public static void Main()
    {
        Console.WriteLine("Test!");
    }
}

Next I'm creating simple third party library with single class which builds to ThirdPartyLibrary.dll:

using System;

namespace ThirdPartyLibrary
{
    public class MyPrint
    {
        public static void Print(string s)
        {
            Console.WriteLine("Third party prints: " + s);
        }
    }
}

Now I wish to replace Console.WriteLine method call to MyPrint.Print method call from my library and get result code something like:

using System;
using ThirdPartyLibrary;

public class Program
{
    public Program()
    {
    }

    public static void Main()
    {
        MyPrint.Print("Test!");
    }
}

As far as I understand I must read my ThirdPartyLibrary.dll file, then reflect it somehow to get all types from it and then it will be possible to use MyPrint type. Finally I wish to be able to path references as myCompiler.exe arguments like using csc.exe.

So the questions are:

  • How to do it?
  • How all this named? (I can't understand what to google)
  • May be I should use some other frameworks, instead of Reflection to do all this?
  • Any other suggestions...
Was it helpful?

Solution

The only change here is the target method:

var targetMethod = Assembly.LoadFrom("ThirdPartyLibrary.dll")
    .GetType("ThirdPartyLibrary.MyPrint")
    .GetMethod("Print", new [] {typeof(string)});
...
il.Emit(OpCodes.Ldstr, "Test!");
il.Emit(OpCodes.Call, targetMethod);
il.Emit(OpCodes.Ret);

So instead of using Console.WriteLine, we call MyPrint.Print.

  • How to do it? A: like the above
  • How all this named? A: IL-generation, meta-programming, reflection
  • May be I should use some other frameworks, instead of Reflection to do all this? A: reflection covers most scenarios; I use IKVM.Reflection.dll in a few places, because it allows me to do cross-framework targeting, but you probably don't need that. What you might find handy is Sigil, which takes away a lot of the pain of IL-generation (unbalanced stacks, etc) by giving you sane error reports at the time you generate (rather than when you execute). Mono.Cecil is also interesting
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top