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 <[email protected]>
>> 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 [email protected].
>>> 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 [email protected].
For more options, visit https://groups.google.com/d/optout.