Hi,

As far as I know, there is no easy way to do this, though I would love
if someone proved me wrong. I'm working on an async method
transformation that extracts method bodies into Cecil-generated nested
classes. I wrote a couple of extension methods to fix up the generic
type tokens. Basically, you will need to inspect every instruction
that emits a type or method reference in the IL and replace any
generic type tokens with the correct ones from the new method you
created. You can use the methods I wrote to do that replacement if
you'd like:

                public static TypeReference CopyGeneric (this TypeReference 
type,
IGenericParameterProvider newBasis,
IDictionary<IGenericParameterProvider,int> skewSet)
                {
                        if (!newBasis.HasGenericParameters)
                                return type;
                                        
                        int skew;
                        var genParam = type as GenericParameter;
                        if (genParam != null && skewSet.TryGetValue 
(genParam.Owner, out skew)) {
                                return newBasis.GenericParameters 
[skew+genParam.Position];
                        }
                        
                        var genInst = type as GenericInstanceType;
                        if (genInst != null) {
                                
                                var args = new TypeReference 
[genInst.GenericArguments.Count];
                                for (int i = 0; i < args.Length; i++)
                                        args [i] = genInst.GenericArguments 
[i].CopyGeneric (newBasis, skewSet);
                                
                                return genInst.ElementType.MakeGeneric (args);
                        }
                        
                        return type;
                }
                
                public static MethodReference CopyGeneric (this MethodReference
method, IGenericParameterProvider newBasis,
IDictionary<IGenericParameterProvider,int> skewSet)
                {
                        var fixedMethod = method;
                        
                        if (newBasis.HasGenericParameters) {
                        
                                var genInst = method as GenericInstanceMethod;
                                if (genInst != null) {
                                
                                        var args = new TypeReference 
[genInst.GenericArguments.Count];
                                        for (int i = 0; i < 
genInst.GenericArguments.Count; i++)
                                                args [i] = 
genInst.GenericArguments [i].CopyGeneric (newBasis, skewSet);
                                        
                                        fixedMethod = 
genInst.ElementMethod.MakeGeneric (args);
                                        
                                } else if (method.DeclaringType is 
GenericInstanceType)
                                        fixedMethod = method.MakeGeneric
(method.DeclaringType.CopyGeneric (newBasis, skewSet));
                        }
                        
                        return fixedMethod;
                }

Pass your new method as newBasis. If it is in the same class as the
original method, you should only have to add the original method to
skewSet with a skew of 0. Don't forget to module.Import the references
returned by these methods.

You will also need the MakeGeneric methods, which I believe I
copied/adapted from either Cecil.Rocks or one of Jb's earlier posts to
this list:

                public static MethodReference MakeGeneric (this MethodReference
method, params TypeReference [] args)
                {
                        if (args.Length == 0)
                                return method;
                        
                        if (method.GenericParameters.Count != args.Length)
                                throw new ArgumentException ("Invalid number of 
generic type
arguments supplied");
                        
                        var genericTypeRef = new GenericInstanceMethod (method);
                        foreach (var arg in args)
                                genericTypeRef.GenericArguments.Add (arg);
                        
                        return genericTypeRef;
                }

                public static MethodReference MakeGeneric (this MethodReference
method, TypeReference declaringType)
                {
                    var reference = new MethodReference (method.Name,
method.ReturnType, declaringType);
                        reference.CallingConvention = method.CallingConvention;
                        reference.HasThis = method.HasThis;
                        reference.ExplicitThis = method.ExplicitThis;
                        
                    foreach (var parameter in method.Parameters)
                        reference.Parameters.Add (new ParameterDefinition
(parameter.ParameterType));
                
                    return reference;
                }

This isn't pretty and it's not fun, but it has worked for me so far.
Hope it helps you too!

Best,
Alex

On Mon, May 2, 2011 at 12:46 AM, Hendry Luk <[email protected]> wrote:
> I'll give more context about what I'm doing.
> I'm developing an AOP tool, and as part of the IL transformation, I need to
> extract out particular instructions from the original method into another
> cecil-generated method, to be passed as a callback-delegate so that the
> aspect can execute the original instruction during runtime.
> This 'transformation pattern' was "inspired" (you may say "stolen") from
> AspectJ, which does pretty much the same thing in principle.
>
> All works pretty well in ordinary methods so far, except that I just
> realized that I've forgotten one important difference between .net dan java:
> .net has true generics.
>
> What it means is that I can't just copy the instruction as is to the new
> method (as we can do in Java), instead I need to make some extra adjustments
> to "repair" all references to open-generics within the copied instructions.
> So I wonder if there's a better way to do some analytical work on the IL
> instructions to find all references to open-generics, or better, if someone
> might have created a utility to repair generic references (Cecil.Rocks?).
>
> Thanks
>
>
> On Mon, May 2, 2011 at 5:25 PM, Hendry Luk <[email protected]> wrote:
>>
>> Hello,
>> I'm using cecil to pull out several lines of code out of a method body
>> into a new "clone" method, and I'm encoutering a problem with updating its
>> generic references. E.g.:
>> public static void OriginalMethod<T, Y>()
>> {
>>     BlahBlah1(typeof(T));
>>     BlahBlah2<T>.SomeField = (Y) something;
>>     BlahBlah3<T,Y>();
>>     Blah4<T>.Something<Y>();
>> }
>>
>> Is going to be weaved into:
>>
>> public static void OriginalMethod<T, Y>()
>> {
>>     BlahBlah1(typeof(T));
>>     BlahBlah2<T>((Y)something);
>>     ClonedMethod<T, Y>(); // Extracted out
>>     Blah4<T>.Something<Y>();
>> }
>>
>> public static void ClonedMethod<T, Y>()
>> {
>>     BlahBlah3<T,Y>();
>> }
>>
>> You see, there's one single instruction that I extract out into a
>> cloned-method. However, it's generic references are still pointing to the
>> old generic arguments (from the original method).
>> Is there an easy way in mono-cecil to walk through our existing
>> instructions for all their generic references, and re-resolve them to point
>> to the new generic provider (i.e. the new method)?
>>
>> PS: The shape of the instruction is quite hard to determine (e.g. I just
>> gave 4 possible ways of how the instruction may reference to the generics).
>> The only way I could think of at the moment is to handle each possible
>> shape of instruction operand (i.e. TypeReference, MemberReference,
>> GenericInstanceMethod, GenericInstanceType, etc) and try to handle every
>> possible way that our generic parameter might get referenced, and fix them
>> appropriately. But that's very error-prone. Is there any easier way to do
>> that? Something like walker or visitor-pattern that enables me to analyse my
>> instructions for any reference to the generic parameter.
>>
>> Thanks before
>>
>>
>
> --
> --
> mono-cecil

-- 
--
mono-cecil

Reply via email to