Thanks Alex, that's very helpful. I was writing something very similar too,
plus one more to cater for (field-reference).
I'm not 100% convinced if I have already covered all possible scenarios, but
I think I would just add gradually as bugs are discovered ;)
Cheers

On Mon, May 2, 2011 at 6:44 PM, Alex Corrado <[email protected]>wrote:

> 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