Some preliminary info
Given the following C# function:
public static void func<T>(T t)
{
System.Console.WriteLine(t);
}
It is compiled into the following CIL:
.method public hidebysig static void func<T>(!!T t) cil managed
{
ldarg.0
box !!T
call void [mscorlib]System.Console::WriteLine(object)
ret
}
The signature of the above method is 10 01 01 01 1E 00
where:
10 - Calling convention (IMAGE_CEE_CS_CALLCONV_GENERIC) 01 - Function generic arguments count (which is 1) 01 - Function arguments count (which is 1) 01 - Return type (ELEMENT_TYPE_VOID) 1E - First argument type (ELEMENT_TYPE_MVAR) 00 - Index of above MVAR (which is 0)
Also see the following instruction with it's actual byte code:
box !!T - 8C 1B000001
1B000001
points to the first entry in the TypeSpec table, which points to the blob 02 1E 00
where:
02 - Blob length 1E - Type type (ELEMENT_TYPE_MVAR) 00 - Index of above MVAR (which is 0)
As we can see, the method signature contains the generic argument in a descriptive way, where we have the actual type signature.
However, when using an OpCode which requires a TypeDef/Ref/Spec, a TypeSpec is supplied, and the TypeSpec points to a signature with the type information.
So my question is:
I'm writing a profiler which does some IL Rewriting, and given the function signature, I would like to add some OpCodes to the function body which will operate with the arguments.
Using the IMetaDataImport2
interface, how can I get the TypeSpec token I would need for a given generic parameter?
I can see 2 options:
- Iterating over EnumTypeSpecs till I find a signature that matches
- Using the
IMetaDataEmit
interface to create a new TypeSpec
However, for obvious reasons, I would like to avoid those 2 options and choose a more sensible alternative.