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] > <javascript:>> 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] <javascript:>. >> 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.
