Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why setting a read only field with dynamic method causes error in this class?

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?

like image 439
konrad.kruczynski Avatar asked Aug 13 '15 13:08

konrad.kruczynski


1 Answers

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.

The plot thickens

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..

like image 114
Roger Johansson Avatar answered Sep 22 '22 09:09

Roger Johansson