Call a call to the System.Lazy <T> constructor with Mono.Cecil
I am trying to emit a method that creates an instance of System.Lazy and with the PEVerify error "Invalid token" in the line newobj instance void class [mscorlib]System.Lazy`1<class Example.ExpensiveType>::.ctor(class [mscorlib]System.Func`1<class Example.ExpensiveType>)
Elsewhere with ILDasm, I see that the correct call would look like this:
newobj instance void class [mscorlib]System.Lazy`1<class Example.IHeater>::.ctor(class [mscorlib]System.Func`1<!0>) Unfortunately, I do not understand how to reproduce this using the Mono.Cecil API. Can anyone help with generics?
Here is what I still have:
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); Any help would be greatly appreciated - I'm at a standstill!
I found the answer buried in a multi-year archive of mailing lists (thanks to Gabor Kozar!). I incorrectly created / imported generic types and their methods. The code that correctly loads the Lazy<T> and Func<T> types follows:
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 The key to the above is the MakeHostInstanceGeneric extension MakeHostInstanceGeneric , which is defined as
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; }