Emit call to System.Lazy constructor with Mono.Cecil

前端 未结 1 1158
礼貌的吻别
礼貌的吻别 2021-01-02 08:49

I\'m trying to emit a method that instantiates a System.Lazy and failing with a PEVerify error of \"Invalid token\", at the line newobj instance void class [mscorl

1条回答
  •  一整个雨季
    2021-01-02 09:07

    I've discovered the answer buried in a years-old mailing list archive (thanks to Gábor Kozár!). I was not properly creating/importing generic types and their methods. The code that properly loads the Lazy and Func types follows:

    var genericArgument = lazyElementType;
    var funcType = ModuleDefinition.ImportReference(typeof(Func<>)).MakeGenericInstanceType(genericArgument);
    var funcCtor =
        ModuleDefinition.ImportReference(funcType.Resolve()
                                        .Methods.First(m => m.IsConstructor && m.Parameters.Count == 2))
                        .MakeHostInstanceGeneric(genericArgument);
    
    var lazyType = ModuleDefinition.ImportReference(typeof(Lazy<>)).MakeGenericInstanceType(genericArgument);
    var lazyCtor =
        ModuleDefinition.ImportReference(lazyType.Resolve()
                                        .GetConstructors()
                                        .First(m => m.Parameters.Count == 1
                                                 && m.Parameters[0].ParameterType.Name.StartsWith("Func")))
                        .MakeHostInstanceGeneric(genericArgument);
    
    // Method body as above
    

    Key to the above is the extension method 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;
    }
    

    0 讨论(0)
提交回复
热议问题