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