I have a .NET library and I am trying to understand more about the inner workings of .NET. So I am going through ECMA-335 specification and utilizing the CFF Explorer VII.
My question is, in the TypeSpec table the Signature is an index in to the Blob heap and should be a TypeSpec signature as described in section 23.2.14. Which means it can be a PTR, FNPTR, ARRAY, SZARRAY, GENERICINSTANCE (ELEMENT_TYPE_ removed for brevity).
However I have two entries in this TypeSpec table that are seemingly not referenced by any other table in the metadata and are of type VAR 0x13 and MVAR 0x1e.
This assembly is compiled in VS2010 against .NET 4.
The ECMA-335 would lead me to believe this is an error but it has been compiled using the MS C# compiler.
Does anyone know what they are and what they mean?
Update:
After lots of messing around this code creates those two entries in the TypeSpec table.
public class AllOutputTypesClass<T> {
public void GenericMethod<N>(N anItem) {
string s = anItem.ToString();
}
public string GenericMethod<N>(T anItem, N secondItem) {
return anItem.ToString();
}
}
Both TypeSpec tokens in your example are referenced from the IL in the method implementations, not from the metadata. For example, the IL for one of your methods looks like this:
.method public hidebysig instance string
GenericMethod<N>(!T anItem,
!!N secondItem) cil managed
{
// Code size 19 (0x13)
.maxstack 1
.locals init (string V_0)
IL_0000: nop
IL_0001: ldarga.s anItem
IL_0003: constrained. !T
IL_0009: callvirt instance string [mscorlib]System.Object::ToString()
IL_000e: stloc.0
IL_000f: br.s IL_0011
IL_0011: ldloc.0
IL_0012: ret
} // end of method AllOutputTypesClass`1::GenericMethod
The TypeSpec token is used to represent the "!T" in the constrained prefix. You'll see a similar reference to the other TypeSpec token in the body of the other method.
If you use ILDasm you can get a good breakdown of the methods, go to the View -> Meta Info -> select More Hex. Then go to the View -> Meta Info -> Show!
Here is the breakdown for the second method:
Method #2 (06000002)
-------------------------------------------------------
MethodName: GenericMethod (06000002)
Flags : [Public] [HideBySig] [ReuseSlot] (00000086)
RVA : 0x0000206c
ImplFlags : [IL] [Managed] (00000000)
CallCnvntn: [DEFAULT]
hasThis
generic
Type Arity:1
ReturnType: String
2 Arguments
Argument #1: Var!0
Argument #2: MVar!!0
Signature : 30 01 02 0e 13 00 1e 00
1 Generic Parameters
(0) GenericParamToken : (2a000003) Name : N flags: 00000000 Owner: 06000002
2 Parameters
(1) ParamToken : (08000002) Name : anItem flags: [none] (00000000)
(2) ParamToken : (08000003) Name : secondItem flags: [none] (00000000)
If you scroll down further you'll see the TypeSpec table entries too.
TypeSpec : MVar!!0
Signature: 1e 00
TypeSpec : Var!0
Signature: 13 00
What ILdasm shows above is the VAR (13 00) is the generic !T passed at the class level (generic param in type definition) and the MVAR (1e 00) is the generic !!N passed at the method level (generic param in method definition).
However ... you already know that much, Orion has your answer - the entries in the TypeSpec table are being used in the IL. One entry is used in one method and the other is the other method. You can see this in ILdasm when you look at the methods:
.method /*06000001*/ public hidebysig instance void
'GenericMethod'<'N'>(!!'N' 'anItem') cil managed
// SIG: 30 01 01 01 1E 00
{
// Method begins at RVA 0x2050
// Code size 16 (0x10)
.maxstack 1
.locals /*11000001*/ init ([0] string 's')
IL_0000: /* 00 | */ nop
IL_0001: /* 0F | 01 */ ldarga.s 'anItem'
IL_0003: /* FE16 | (1B)000001 */ constrained. !!'N'/*1B000001*/
IL_0009: /* 6F | (0A)000011 */ callvirt instance string ['mscorlib'/*23000001*/]'System'.'Object'/*01000001*/::'ToString'() /* 0A000011 */
IL_000e: /* 0A | */ stloc.0
IL_000f: /* 2A | */ ret
} // end of method 'AllOutputTypesClass`1'::'GenericMethod'
and
.method /*06000002*/ public hidebysig instance string
'GenericMethod'<'N'>(!'T' 'anItem',
!!'N' 'secondItem') cil managed
// SIG: 30 01 02 0E 13 00 1E 00
{
// Method begins at RVA 0x206c
// Code size 19 (0x13)
.maxstack 1
.locals /*11000001*/ init ([0] string 'CS$1$0000')
IL_0000: /* 00 | */ nop
IL_0001: /* 0F | 01 */ ldarga.s 'anItem'
IL_0003: /* FE16 | (1B)000002 */ constrained. !'T'/*1B000002*/
IL_0009: /* 6F | (0A)000011 */ callvirt instance string ['mscorlib'/*23000001*/]'System'.'Object'/*01000001*/::'ToString'() /* 0A000011 */
IL_000e: /* 0A | */ stloc.0
IL_000f: /* 2B | 00 */ br.s IL_0011
IL_0011: /* 06 | */ ldloc.0
IL_0012: /* 2A | */ ret
} // end of method 'AllOutputTypesClass`1'::'GenericMethod'
The 0x1b table is the TypeSpec, so the following lines from the methods above show the usage of the 2 rows you were wondering about:
IL_0003: /* FE16 | (1B)000001 */ constrained. !!'N'/*1B000001*/
and
IL_0003: /* FE16 | (1B)000002 */ constrained. !'T'/*1B000002*/
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With