Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to store value into a generic class field (Mono.Cecil)

I'm using Mono.Cecil for rewriting some assemblies. Given a generic class with a string field for example. I want to generate code into a method of this class which writes into this field. This is the code to achieve that:

var origInstanceField = type.Fields.First(fld => fld.Name.Equals("_original"));
InsertBeforeReturn(instanceMethod.Body, new[]
{
   Instruction.Create(OpCodes.Ldarg_0),
   Instruction.Create(OpCodes.Ldstr, "genCodeToOriginal"),
   Instruction.Create(OpCodes.Stfld, origInstanceField)
});

For non-generic classes it works like a charm. For generic classes it creates bad IL. If you take a look on the decompiled IL you can spot the difference compared to the same instructions emitted by the compiler.

.method public hidebysig instance void FillFields() cil managed
{
.maxstack 8
L_0000: nop 
L_0001: ldarg.0 
L_0002: ldstr "non-gen code to non-gen field"
L_0007: stfld string TestConsole.GenericClass`1<!T>::_original
L_0017: ldarg.0 
L_0018: ldstr "genCodeToOriginal"
L_001d: stfld string TestConsole.GenericClass`1::_original
L_0022: ret 
}

The field reference points to the open generic type [GenericClass<>] and not to the to be constructed type [GenericClass< T>]. I tried also with a static field with the same result.
Any idea how can I get to the right FieldDefinition?

like image 469
csnemes Avatar asked Mar 31 '15 15:03

csnemes


1 Answers

I've managed to find a solution by looking at the source code of Anotar (https://github.com/Fody/Anotar). The trick is to create a field reference with a type definition refering to an instantiated generic, not the original one.

var declaringType = new GenericInstanceType(definition.DeclaringType);
foreach (var parameter in definition.DeclaringType.GenericParameters)
{
     declaringType.GenericArguments.Add(parameter);
}
return new FieldReference(definition.Name, definition.FieldType, declaringType);

Using this transformation on the FieldDefinition coming from type gives the right FieldReference. Feeding the generic parameters to generic arguments is a little odd but makes sense.

like image 126
csnemes Avatar answered Oct 30 '22 09:10

csnemes