Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ILGenerator.DeclareLocal() takes a type of a class not yet compiled

Toying with making a compiler for my own language, I'm trying to generate some MSIL code using the Reflection.Emit framework. It works fine when using int when I declare local variables. However, when I want to declare a local variable of a type I have not yet compiled I get into trouble since the DeclareLocal() takes a Type as argument. That is my uncompiled class, say A, still needs to be defined using

 assemblyBuilder = Thread.GetDomain().DefineDynamicAssembly(assemName, AssemblyBuilderAccess.RunAndSave);
 module = assemblyBuilder.DefineDynamicModule(Filename); 
 module.DefineType(name, TypeAttributes.Public | TypeAttributes.Class)

So how will I ever be able to compile the following program

class A {
    void M() { B b = new B(); }
}
class B
    void M() { A a = new A(); }
}
like image 286
Carlo V. Dango Avatar asked Sep 14 '12 22:09

Carlo V. Dango


1 Answers

The primary insight you need here is that TypeBuilder derives from Type. So, even if you didn't finalize a type yet (by calling CreateType()), you can use it to declare a local variable in another type.

One more barrier I encountered is that GetConstructor() on an unfinished TypeBuilder doesn't work (it throws an exception). But if you create the default constructor explicitly, you can call it through the ConstructorBuilder.

static void Main()
{
    var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(
        new AssemblyName("foo"), AssemblyBuilderAccess.RunAndSave);
    var module = assemblyBuilder.DefineDynamicModule("foo.dll");
    var aType = module.DefineType(
        "A", TypeAttributes.Public | TypeAttributes.Class);
    var bType = module.DefineType(
        "B", TypeAttributes.Public | TypeAttributes.Class);
    var aCtor = aType.DefineDefaultConstructor(MethodAttributes.Public);
    var bCtor = bType.DefineDefaultConstructor(MethodAttributes.Public);
    CreateMethodM(aType, bType, bCtor);
    CreateMethodM(bType, aType, aCtor);
    aType.CreateType();
    bType.CreateType();
    assemblyBuilder.Save("foo.dll");
}

static void CreateMethodM(
    TypeBuilder thisType, Type otherType, ConstructorInfo otherCtor)
{
    var method = thisType.DefineMethod(
        "M", MethodAttributes.Private, typeof(void), Type.EmptyTypes);
    var il = method.GetILGenerator();
    var local = il.DeclareLocal(otherType);
    il.Emit(OpCodes.Newobj, otherCtor);
    il.Emit(OpCodes.Stloc, local);
    il.Emit(OpCodes.Ret);
}
like image 75
svick Avatar answered Oct 14 '22 16:10

svick