One can store in a read only field of a class using strfld
op code in dynamic method if it has its owner set to that class and JIT checks are turned off. An example is here. This approach, however, failed to work with the class that comes from F#, namely FSharpOption
. Please analyse an example below:
using Microsoft.FSharp.Core;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;
#if true
using MyType = Microsoft.FSharp.Core.FSharpOption<string>;
#else
using MyType = System.Tuple<string>;
#endif
namespace ConsoleApplication27
{
class Program
{
static void Main(string[] args)
{
var something = new MyType("test");
var dynMethod = new DynamicMethod("ChangeField", MethodAttributes.Public | MethodAttributes.Static, CallingConventions.Standard, typeof(void), new [] { typeof(MyType) }, typeof(MyType), true);
var generator = dynMethod.GetILGenerator();
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Ldstr, "success");
generator.Emit(OpCodes.Stfld, typeof(MyType).GetFields(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public)[0]);
generator.Emit(OpCodes.Ret);
var method = (Action<MyType>) dynMethod.CreateDelegate(typeof(Action<MyType>));
method(something);
Console.WriteLine(typeof(MyType).GetProperties()[0].GetGetMethod().Invoke(something, new object[0]));
}
}
}
First of all, you have to reference FSharp.Core library to run it. Then, by changing #if true
to #if false
you can switch between writing a read only field fo Tuple
and FSharpOption
. For the former it works perfectly even though both have similar structure, that is a single read only field accessible via property. For the latter it causes verification failure. Why is that so?
Very very late response.
This is very odd.
By replacing the MyType
with the module of the field type. it all starts to work
var dynMethod = new DynamicMethod("ChangeField", MethodAttributes.Public | MethodAttributes.Static,
CallingConventions.Standard, typeof(void), new[] {typeof(MyType)}, typeof(MyType), true);
Becomes:
var dynMethod = new DynamicMethod("ChangeField", MethodAttributes.Public | MethodAttributes.Static,
CallingConventions.Standard, typeof(void), new[] {typeof(MyType)}, typeof(string).Module, true);
That is:
typeof(MyType), true);
becomes
typeof(string).Module, true);
The type of the field being set is string
so we take the module of that.
I would love to hear a better explanation on why this is.
it turns out, that typeof(string).module
makes it work for any type.
even if I define my own type like this:
using System;
using System.Reflection;
using System.Reflection.Emit;
#if true
using MyType = Microsoft.FSharp.Core.FSharpOption<string>;
#else
using MyType = System.Tuple<ConsoleApplication27.Poco>;
#endif
namespace ConsoleApplication27
{
public class Poco
{
}
class Program
{
static void Main(string[] args)
{
var something = new MyType(null);
var dynMethod = new DynamicMethod("ChangeField", MethodAttributes.Public | MethodAttributes.Static,
CallingConventions.Standard, typeof(void), new[] {typeof(MyType)}, typeof(string).Module, true);
var generator = dynMethod.GetILGenerator();
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Ldnull);
generator.Emit(OpCodes.Stfld,
typeof(MyType).GetFields(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public)[0]);
generator.Emit(OpCodes.Ret);
var method = (Action<MyType>) dynMethod.CreateDelegate(typeof(Action<MyType>));
method(something);
Console.WriteLine(typeof(MyType).GetProperties()[0].GetGetMethod().Invoke(something, new object[0]));
}
}
}
Now nothing makes sense. the FSharpOption type lives in the F# module. The Poco class lives in my program module. and still it all works when the module passed is the corelib module. But not if I pass any of the two (above) that would make sense..
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