2013-05-08 14 views
7

Próbuję emitować metodę, która instancję System.Lazy i niepowodzeniem z błędem PEVerify z „Nieprawidłowy token”, w wierszu newobj instance void class [mscorlib]System.Lazy`1<class Example.ExpensiveType>::.ctor(class [mscorlib]System.Func`1<class Example.ExpensiveType>)Emit wywołanie System.Lazy <T> konstruktora z Mono.Cecil

Szukam gdzie indziej z ILDasm, widzę, że właściwa rozmowa będzie wyglądać następująco:

newobj  instance void class [mscorlib]System.Lazy`1<class Example.IHeater>::.ctor(class [mscorlib]System.Func`1<!0>) 

Niestety, jestem w rozterce co do sposobu odtworzenia tego z API Mono.Cecil. Czy ktoś może pomóc z rodzajami leków?

Oto co mam do tej pory:

var get = new MethodDefinition(
      "Get", 
      MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual, 
      ModuleDefinition.TypeSystem.Object); 

var funcType = new GenericInstanceType(ImportedTypes.FuncOfT); 
funcType.GenericArguments.Add(lazyElementType); 

var funcTypeCtor = new MethodReference(".ctor", ModuleDefinition.TypeSystem.Void, funcType); 
funcTypeCtor.Parameters.Add(new ParameterDefinition(ModuleDefinition.TypeSystem.Object)); 
funcTypeCtor.Parameters.Add(new ParameterDefinition(ModuleDefinition.TypeSystem.IntPtr)); 
funcTypeCtor.HasThis = true; 
funcTypeCtor = ModuleDefinition.Import(funcTypeCtor); 

var lazyTypeCtor = new MethodReference(".ctor", ModuleDefinition.TypeSystem.Void, lazyType); 
var parameterDefinition = new ParameterDefinition(funcType); 
lazyTypeCtor.Parameters.Add(parameterDefinition); 
lazyTypeCtor.HasThis = true; 
lazyTypeCtor = ModuleDefinition.Import(lazyTypeCtor); 

il = get.Body.GetILProcessor(); 
il.Emit(OpCodes.Ldarg_0); 
il.Emit(OpCodes.Ldftn, getTypedValue); 
il.Emit(OpCodes.Newobj, funcTypeCtor); 
il.Emit(OpCodes.Newobj, lazyTypeCtor); // This leads to the invalid token 
il.Emit(OpCodes.Ret); 
lazyBinding.Methods.Add(get); 

Każda pomoc będzie mile widziane - Jestem zakłopotany!

Odpowiedz

9

Odkryłem odpowiedź ukrytą w archiwum listy mailingowej lat (dzięki Gábor Kozár!). Nie tworzyłem/nie importowałem generycznych typów i ich metod. Kod, który prawidłowo ładuje Lazy<T> i Func<T> typy następująco:

var genericArgument = lazyElementType; 
var funcType = ModuleDefinition.Import(typeof(Func<>)).MakeGenericInstanceType(genericArgument); 
var funcCtor = 
    ModuleDefinition.Import(funcType.Resolve() 
            .Methods.First(m => m.IsConstructor && m.Parameters.Count == 2)) 
        .MakeHostInstanceGeneric(genericArgument); 

var lazyType = ModuleDefinition.Import(typeof(Lazy<>)).MakeGenericInstanceType(genericArgument); 
var lazyCtor = 
    ModuleDefinition.Import(lazyType.Resolve() 
            .GetConstructors() 
            .First(m => m.Parameters.Count == 1 
              && m.Parameters[0].ParameterType.Name.StartsWith("Func"))) 
        .MakeHostInstanceGeneric(genericArgument); 

// Method body as above 

Klucz do powyższego jest metoda rozszerzenie MakeHostInstanceGeneric, który jest zdefiniowany jako

public static MethodReference MakeHostInstanceGeneric(
            this MethodReference self, 
            params TypeReference[] args) 
{ 
    var reference = new MethodReference(
     self.Name, 
     self.ReturnType, 
     self.DeclaringType.MakeGenericInstanceType(args)) 
    { 
     HasThis = self.HasThis, 
     ExplicitThis = self.ExplicitThis, 
     CallingConvention = self.CallingConvention 
    }; 

    foreach (var parameter in self.Parameters) { 
     reference.Parameters.Add(new ParameterDefinition(parameter.ParameterType)); 
    } 

    foreach (var genericParam in self.GenericParameters) { 
     reference.GenericParameters.Add(new GenericParameter(genericParam.Name, reference)); 
    } 

    return reference; 
}