Hey All,

I think I figured out what I was doing wrong.

Here is the snippet of code that was incorrect:
var proxyReturnRefOpen = injectorModuleDef.GetType(typeof(Injector).FullName
).Methods
        .First(m => m.FullName == "T TestInjector.Injector::ReturnProxy(T)"
);
var genericArg = method.ReturnType.Resolve();
var proxyReturnRefClosed = new GenericInstanceMethod(proxyReturnRefOpen);
proxyReturnRefClosed.GenericArguments.Add(genericArg);
var proxyReturnInstruction = processor.Create(OpCodes.Call, method.Module.
Import(proxyReturnRefClosed));

The problem was, that I was passing proxyReturnRefClosed into the Import 
method. What I should have done, was pass proxyReturnRefOpen to the Import 
method, and then instantiate the GenericInstanceMethod class based on the 
MethodReference returned by Import. Here is a correct implementation that 
doesn't throw an exception:
var genericArg = method.ReturnType;
var proxyReturnRef = injectorModuleDef.GetType(typeof(Injector).FullName).
Methods
        .First(m => m.FullName == "T TestInjector.Injector::ReturnProxy(T)"
);
var proxyReturnRefOpen = method.Module.Import(proxyReturnRef);
var proxyReturnRefClosed = new GenericInstanceMethod(proxyReturnRefOpen);
proxyReturnRefClosed.GenericArguments.Add(genericArg);
var proxyReturnInstruction = processor.Create(OpCodes.Call, 
proxyReturnRefClosed);

Thanks for your help guys
-- Camden Reslink

On Monday, October 23, 2017 at 12:52:44 AM UTC-4, Camden Reslink wrote:
>
> Hey Jb,
>
> Thank you for your reply. When I try to change the code, so it reads:
>
> var genericArg = method.ReturnType; // No Resolve() called here.
>
> When the value of method.ReturnType defined above has either of the 
> following properties true: IsGenericInstance, or IsGenericParameter, I 
> receive a null reference exception originating from Cecil on the following 
> line:
>
> proxyReturnInstruction = processor.Create(OpCodes.Call, method.Module.
> Import(proxyReturnRefClosed));
>
> Here is the stack trace:
>    at Mono.Cecil.ImportGenericContext.MethodParameter(String method, Int32 
> position)
>    at Mono.Cecil.MetadataImporter.ImportTypeSpecification(TypeReference 
> type, ImportGenericContext context)
>    at Mono.Cecil.MetadataImporter.ImportType(TypeReference type, 
> ImportGenericContext context)
>    at Mono.Cecil.MetadataImporter.ImportTypeSpecification(TypeReference 
> type, ImportGenericContext context)
>    at Mono.Cecil.MetadataImporter.ImportType(TypeReference type, 
> ImportGenericContext context)
>    at Mono.Cecil.MetadataImporter.ImportMethodSpecification(
> MethodReference method, ImportGenericContext context)
>    at Mono.Cecil.MetadataImporter.ImportMethod(MethodReference method, 
> ImportGenericContext context)
>    at Mono.Cecil.ModuleDefinition.Import(MethodReference method, 
> IGenericParameterProvider context)
>    at Mono.Cecil.ModuleDefinition.Import(MethodReference method)
>    at Profiler.Inject.Injectors.ReturnInjector.InjectReturnProxy(
> MethodDefinition method)
>
> In the case where the return type that I'm trying to intercept is 
> "System.Collections.Generic.List`1<T>", Here is what the FullName property 
> of proxyReturnRefClosed looks like:
> T TestInjector.Injector::ReturnProxy<System.Collections.Generic.List
> `1<T>>(T)
>
> I'm thinking it should actually look something like:
> System.Collections.Generic.List`1<T> 
> TestInjector.Injector::ReturnProxy<System.Collections.Generic.List`1<T>>(
> System.Collections.Generic.List`1<T>)
>
> Because, the proxy method should be accepting a List<T> and returning a 
> List<T>. Should I be looping through proxyReturnRefClosed.Parameters, and 
> also somehow alter proxyReturnRefClosed.ReturnType (because above it looks 
> like the generic argument gets set correctly, but the types of the 
> parameters/return type stays the same)?
>
> I tried something like that here:
> var genericArg = method.ReturnType;
> var proxyReturnRefClosed = new GenericInstanceMethod(proxyReturnRefOpen);
> proxyReturnRefClosed.GenericArguments.Add(genericArg);
> proxyReturnRefClosed.ReturnType = genericArg;
> proxyReturnRefClosed.Parameters[0].ParameterType = genericArg;
> proxyReturnInstruction = processor.Create(OpCodes.Call, method.Module.
> Import(proxyReturnRefClosed));
>
> This gives me the proxyReturnRefClosed FullName property that I expect:
> System.Collections.Generic.List`1<T> 
> TestInjector.Injector::ReturnProxy<System.Collections.Generic.List`1<T>>(
> System.Collections.Generic.List`1<T>)
>
> But alas, I get the following exception at the same line as the above 
> exception => System.InvalidOperationException: 'Operation is not valid due 
> to the current state of the object.'
> Here is the stack trace for that exception:
>    at Mono.Cecil.ImportGenericContext.MethodParameter(String method, Int32 
> position)
>    at Mono.Cecil.MetadataImporter.ImportTypeSpecification(TypeReference 
> type, ImportGenericContext context)
>    at Mono.Cecil.MetadataImporter.ImportType(TypeReference type, 
> ImportGenericContext context)
>    at Mono.Cecil.MetadataImporter.ImportTypeSpecification(TypeReference 
> type, ImportGenericContext context)
>    at Mono.Cecil.MetadataImporter.ImportType(TypeReference type, 
> ImportGenericContext context)
>    at Mono.Cecil.MetadataImporter.ImportMethod(MethodReference method, 
> ImportGenericContext context)
>    at Mono.Cecil.MetadataImporter.ImportMethodSpecification(
> MethodReference method, ImportGenericContext context)
>    at Mono.Cecil.MetadataImporter.ImportMethod(MethodReference method, 
> ImportGenericContext context)
>    at Mono.Cecil.ModuleDefinition.Import(MethodReference method, 
> IGenericParameterProvider context)
>    at Mono.Cecil.ModuleDefinition.Import(MethodReference method)
>    at Profiler.Inject.Injectors.ReturnInjector.InjectReturnProxy(
> MethodDefinition method)
>
> I've tried debugging/stepping through the Cecil source, but didn't have 
> much luck figuring out where I'm going wrong (although the context variable 
> in the method calls in the stack traces always seems to be null, which I 
> thought was curious). This is a complicated edge case, so any help would be 
> very much appreciated.
>
> Thanks,
> Camden Reslink
>
> On Friday, October 6, 2017 at 3:34:08 PM UTC-4, Jb Evain wrote:
>>
>> Hey Camden,
>>
>> In:
>>
>>     var genericArg = method.ReturnType.Resolve();
>>     var proxyReturnRefClosed = new GenericInstanceMethod(
>> proxyReturnRefOpen);
>>     proxyReturnRefClosed.GenericArguments.Add(genericArg);
>>
>> Why do you Resolve the return type? I imagine you want to return exactly 
>> the type of method.ReturnType, why don't you use that directly? 
>>
>> Jb
>>
>> On Mon, Oct 2, 2017 at 11:41 AM, Camden Reslink <camden...@gmail.com> 
>> wrote:
>>
>>> Hello,
>>>
>>> I'm using version 0.9.6.4 consumed as a NuGet package from nuget.org.
>>>
>>> I'm trying to inject a call to a static method, which intercepts values 
>>> that would have been returned, and logs/traces/manipulates them in some way 
>>> and then returns them. An example of an intercepting method could look like 
>>> this:
>>> public static T1 ReturnProxy<T1>(T1 value)
>>> {
>>>     // Do something useful with value, like logging it.
>>>     var timestamp = DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss.fff",
>>>                                     CultureInfo.InvariantCulture);
>>>     StackTrace stackTrace = new StackTrace();
>>>     var callingMethod = stackTrace.GetFrame(1).GetMethod();
>>>     File.AppendAllLines(@"C:\temp\test_app.txt", new string[] { 
>>> timestamp + ", " + callingMethod.Name + ", " + Newtonsoft.Json.
>>> JsonConvert.SerializeObject(value) });
>>>
>>>     // Now return it just like the calling method would have without 
>>> this method intercepting it
>>>     return value;
>>> }
>>>
>>>
>>> And here is what a before/after would look like for injecting the 
>>> instructions into the calling method:
>>> // This method is in the assembly being injected into
>>>
>>> //BEFORE
>>> public T2 MethodWithAGenericParameter<T2>(int someVariable)
>>> {
>>>     T2 obj = (T2)Activator.CreateInstance(typeof(T2));
>>>     obj.GetType().GetProperty("MyProperty").SetValue(obj, someVariable);
>>>     return obj;
>>> }
>>>
>>> //AFTER
>>> public T2 MethodWithAGenericParameter<T2>(int someVariable)
>>> {
>>>     T2 obj = (T2)Activator.CreateInstance(typeof(T2));
>>>     obj.GetType().GetProperty("MyProperty").SetValue(obj, someVariable);
>>>     return Injector.ReturnProxy<T2>(obj);
>>> }
>>>
>>>
>>> In the case where the return type of the calling method is not generic, 
>>> the following code is working correctly to inject the call instructions 
>>> into the methods:
>>> public static void InjectReturnProxy(MethodDefinition method)
>>> {
>>>     var injectorModuleDef = ModuleDefinition.ReadModule(typeof(Injector
>>> ).Module.FullyQualifiedName);
>>>     var proxyReturnRefOpen = injectorModuleDef.GetType(typeof(Injector).
>>> FullName).Methods
>>>         .First(m => m.FullName == "T 
>>> TestInjector.Injector::ReturnProxy(T)");
>>>     ILProcessor processor = method.Body.GetILProcessor();
>>>     Instruction proxyReturnInstruction = null;
>>>
>>>     var genericArg = method.ReturnType.Resolve();
>>>     var proxyReturnRefClosed = new GenericInstanceMethod(
>>> proxyReturnRefOpen);
>>>     proxyReturnRefClosed.GenericArguments.Add(genericArg);
>>>     proxyReturnInstruction = processor.Create(OpCodes.Call, method.
>>> Module.Import(proxyReturnRefClosed));
>>>
>>>     var returnInstructions = method.Body.Instructions
>>>             .Where(i => i.OpCode.Name == "ret").ToArray();
>>>
>>>     method.Body.OptimizeMacros();
>>>     for (var i = 0; i < returnInstructions.Length; i++)
>>>     {
>>>         var instruction = returnInstructions[i];
>>>         processor.InsertBefore(instruction, proxyReturnInstruction);
>>>     }
>>>     method.Body.SimplifyMacros();
>>> } 
>>>
>>> However, when the return type of the calling method is generic, like the 
>>> MethodWithAGenericParameter example above, I can't seem to get the 
>>> correct return type to pass as a generic type argument to the 
>>> ReturnProxy<T1> static method. The call to method.ReturnType.Resolve() 
>>> gives 
>>> a value of null in that case, and the type of method.ReturnType is 
>>> Mono.Cecil.GenericParameter. I also see problems with a return type of a 
>>> generic array (like T[]). I haven't tried to resolve when the return 
>>> type of the calling method is a generic type parameter at the class level 
>>> instead of the method level, but I suspect I'll have similar issues in that 
>>> case. Any thoughts on how to perform the above injection of a method call 
>>> passing in as the generic argument the generic parameter of the parent 
>>> method? Thanks for any help that could be provided!
>>>
>>> -- 
>>> -- 
>>> --
>>> mono-cecil
>>> --- 
>>> You received this message because you are subscribed to the Google 
>>> Groups "mono-cecil" group.
>>> To unsubscribe from this group and stop receiving emails from it, send 
>>> an email to mono-cecil+...@googlegroups.com.
>>> For more options, visit https://groups.google.com/d/optout.
>>>
>>
>>

-- 
-- 
--
mono-cecil
--- 
You received this message because you are subscribed to the Google Groups 
"mono-cecil" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to mono-cecil+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to