Related to How to mutate a boxed struct using IL I am trying to change the value of a boxed value type but in a generic way, so trying to implement the following method:
The difference is that DynamicMethod
by default requires verifiable code, whereas your own code (including custom IL) is by default allowed to be unverifiable.
You can treat the DynamicMethod
as part of your own module, allowing it to contain unverifiable IL, by specifying the module:
var dynMtd = new DynamicMethod("EvilMutateValueType",
typeof(void), new Type[] { typeof(object), typeof(T) }, typeof(Program).Module);
// Use whatever class you have available here. ^^^^^^^^^^^^^^^^^^^^^^
Despite some other issues in PEVerify making it hard to get good diagnostics, it looks like this is intended to at least not be verifiable:
III.1.8.1.2.2 Controlled-mutability managed pointers
The
readonly.
prefix andunbox
instructions can produce what is called a controlled-mutability managed pointer. Unlike ordinary managed pointer types, a controlled-mutability managed pointer is not verifier-assignable-to (§III.1.8.1.2.3) ordinary managed pointers; e.g., it cannot be passed as a byref argument to a method. At control flow points, a controlled-mutability managed pointer can be merged with a managed pointer of the same type to yield a controlled-mutability managed pointer.Controlled-mutability managed pointers can only be used in the following ways:
- As the object parameter for an
ldfld
,ldflda
,stfld
,call
,callvirt
, orconstrained. callvirt
instruction.- As the pointer parameter to a
ldind.*
orldobj
instruction.- As the source parameter to a
cpobj
instruction.All other operations (including
stobj
,stind.*
,initobj
, andmkrefany
) are invalid.[...]
But it looks like it's intended to still be correct:
III.4.29 stobj – store a value at an address
[...]
Correctness:
Correct CIL ensures that dest is a pointer to
T
and the type of src is verifier-assignable-toT
.[...]
Note that there is no restriction on controlled-mutability managed pointers here, any pointer to T
is allowed.
Therefore, ensuring that no verification happens for your IL is the right way to go.
One solution, is by making an Unbox
method in IL that calls unbox
and returns a ref
to the type contained in the object:
.method public hidebysig static !!T& Unbox<T>(object o) cil managed aggressiveinlining
{
.maxstack 1
ldarg.0
unbox !!T
ret
}
And then using this like:
public static void MutateValueType<T>(object o, T v)
{
ref T ub = ref Unsafe.Unbox<T>(o);
ub = v;
}
This correctly outputs:
var oi = (object)17;
MutateValueType<int>(oi, 43);
Console.WriteLine(oi); // 43
var od = (object)17.7d;
MutateValueType<double>(od, 42.3);
Console.WriteLine(od); // 42.3
NOTE: This requires C# 7 support for ref
returns.
This might be added to say https://github.com/DotNetCross/Memory.Unsafe but it must also be possible with il.Emit
also, so I am looking for that.