but it should get the value of the boxed int's first field (the header) in the second case
You're assuming the ToString
method is passed a pointer, and whatever bytes happen to be pointed to are what gets read. That assumption is wrong.
An instance method of a value type behaves like it has a ref T
"this" parameter. The .NET runtime will make sure this gets passed correctly regardless of whether the value type is boxed, so that the method does not need to give boxed values any special treatment.
Your starting point is correct:
I.8.9.7 Value type definition
When a non-static method (i.e., an instance or virtual method) is called on the value type, its this pointer is a managed reference to the instance, whereas when the method is called on the associated boxed type, the this pointer is an object reference.
However, this happens in such a way that the same ldind.i4
instruction works regardless. Only when it refers to an unmanaged pointer will it simply read bytes.
You found
II.13.3 Methods of value types
Instance and virtual methods of classes shall be coded to expect a reference to an instance of the class as the this pointer. By contrast, instance and virtual methods of value types shall be coded to expect a managed pointer (see Partition I) to an unboxed instance of the value type. The CLI shall convert a boxed value type into a managed pointer to the unboxed value type when a boxed value type is passed as the this pointer to a virtual method whose implementation is provided by the unboxed value type.
which at first glance contradicts the above, but actually says the implementation must simply use whatever magic is necessary to make it work.