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
