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:
void MutateValueType<T>(object o, T v) where T : struct
So the following should be possible:
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
I am failing to get it this to work on .NET Framework (see comment by @hvd that the implementation without typeof(Program).Module
works on other runtimes). I have implemented this as seen below. However, this fails when calling the delegate del
with a:
System.Security.VerificationException: 'Operation could destabilize the runtime.'
Here is the implementation I have come up with:
public static void MutateValueType<T>(object o, T v)
{
var dynMtd = new DynamicMethod("EvilMutateValueType",
typeof(void), new Type[] { typeof(object), typeof(T) });
var il = dynMtd.GetILGenerator();
il.Emit(OpCodes.Ldarg_0); // object
il.Emit(OpCodes.Unbox, typeof(T)); // T&
il.Emit(OpCodes.Ldarg_1); // T (argument value)
il.Emit(OpCodes.Stobj, typeof(T)); // stobj !!T
il.Emit(OpCodes.Ret);
var del = (Action<object, T>)dynMtd.CreateDelegate(typeof(Action<object, T>));
del(o, v);
}
The above should be equivalent to the below IL, that works, but still the above fails, so the question is why this doesn't work.
.method public hidebysig static void Mutate<T>(object o, !!T Value) cil managed aggressiveinlining
{
.maxstack 2
ldarg.0
unbox !!T
ldarg.1
stobj !!T
ret
}
unbox
simply returns a managed pointer to the value, or in other words address of memory containing the value type. Assigning a new value to this should be possible. This should correspond to a method returningref
e.g.ref T Unbox<T>(object o)
, in fact I think this could be implemented in IL and then used to the above. – Catercousinstruct Evil { int i; public override string ToString() { ++i; return i.ToString(); } }
, thenobject o = new Evil();
. Now, the spec requireso.ToString() != o.ToString()
-- that is, the spec requires the runtime to support in-place updates of boxed values. Given that the runtime is required to support that anyway, and given that my example doesn't make use of anything that actually requires co-operation from the value type in question, it seems clear to me that what the OP is after should be possible. – Kucikthis
assignment possible instruct
constructors be generally available. – Deirdrethis = new Evil { i = i + 1 };
to modify the entire struct. Struct assignment has never been limited to constructors. – Kucik