2012-09-14 2 views
7

Mit dem Erstellen eines Compilers für meine eigene Sprache versuche ich, einige MSIL-Code mit dem Reflection.Emit-Framework zu generieren. Es funktioniert gut, wenn ich int verwende, wenn ich lokale Variablen deklariere. Wenn ich jedoch eine lokale Variable eines Typs deklarieren möchte, die ich noch nicht kompiliert habe, stehe ich in Schwierigkeiten, da die DeclareLocal() eine Type als Argument nimmt. Das ist meine kompilierten Klasse, sagt A, noch definiert werden mussILGenerator.DeclareLocal() nimmt einen Typ einer Klasse, die noch nicht kompiliert wurde

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

mit So wie werde ich jemals in der Lage sein, das folgende Programm zu kompilieren

class A { 
    void M() { B b = new B(); } 
} 
class B 
    void M() { A a = new A(); } 
} 
+0

Was würde das helfen? –

+1

@phoog: Er könnte genauso gut auf jemanden warten, der seine Arbeit macht, dann ... –

+0

Ich mache C# nicht wirklich .. es war nur ein Beispiel für zirkuläre Abhängigkeit –

Antwort

7

Die primäre Einsicht Sie hier brauchen, ist, dass TypeBuilder stammt von Type. Selbst wenn Sie einen Typ noch nicht finalisiert haben (indem Sie CreateType() aufrufen), können Sie damit eine lokale Variable in einem anderen Typ deklarieren.

Eine weitere Barriere ich gestoßen ist, dass GetConstructor() an einer unvollendeten TypeBuilder nicht funktioniert (es löst eine Ausnahme). Wenn Sie jedoch den Standardkonstruktor explizit erstellen, können Sie ihn über die ConstructorBuilder aufrufen.

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); 
} 
+1

Argh! Meine Lösung ist so nah bei dir! Ich wurde geblendet, indem ich "t.DecallingType" und nicht einfach "t" nennen wollte. –

+2

'DecellingType' ist etwas anderes, es stellt den Typ dar, in dem dieser Typ deklariert wurde, zum Beispiel, wenn es ein verschachtelter Typ ist. – svick

Verwandte Themen