Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

DynamicMethod and type checks

Can someone explain or point to explanation why runtime types check not occurs in sample below - string property can be set to any type value ...
Stuck with this in very unexpected place and was really surprised

using System;
using System.Reflection;
using System.Reflection.Emit;

namespace Dynamics
{
internal class Program
    {
    private static void Main(string[] args)
    {
        var a = new A();
        a.Name = "Name";
        Console.WriteLine(a.Name.GetType().Name);

        PropertyInfo pi = a.GetType().GetProperty("Name");          

        DynamicMethod method = new DynamicMethod(
                "DynamicSetValue", // NAME
                null, // return type
                new Type[] 
                            {
                                typeof(object), // 0, objSource
                                typeof(object), // 1, value
                            }, // parameter types
                typeof(Program), // owner
                true); // skip visibility

        ILGenerator gen = method.GetILGenerator();
        gen.Emit(OpCodes.Ldarg_0);
        gen.Emit(OpCodes.Ldarg_1);
        gen.Emit(OpCodes.Call, pi.GetSetMethod(true));
        gen.Emit(OpCodes.Ret);

        SetValue setMethod = (SetValue)method.CreateDelegate(typeof(SetValue));

        int val = 123;
        setMethod(a, val);
        Console.WriteLine(a.Name.GetType().Name);

        A anotherA = new A();
        anotherA.Name = "Another A";
        setMethod(a, anotherA);
        Console.WriteLine(a.Name.GetType().Name);
    }
}

public class A
{
    public string Name { get; set; }
}

public delegate void SetValue(object obj, object val);
}
like image 839
Alex A Avatar asked Jul 31 '13 13:07

Alex A


1 Answers

I did a little experiment: Added a method to your class:

    static void SetValue1(A a, object v)
    {
        a.Name = (string)v;
    }

Doing SetValue1(a, 123); throws InvalidCastException of course. I then disassembled the code using ildasm.exe. SetValue1 looks like this:

.method private hidebysig static void  SetValue1(class ConsoleApplication2.A a,
                                                   object v) cil managed
  {
    // Code size       15 (0xf)
    .maxstack  8
    IL_0000:  nop
    IL_0001:  ldarg.0
    IL_0002:  ldarg.1
    IL_0003:  castclass  [mscorlib]System.String // <--- replace this with nop
    IL_0008:  callvirt   instance void ConsoleApplication2.A::set_Name(string)
    IL_000d:  nop
    IL_000e:  ret
  } // end of method Program::SetValue1

Ok, let's replace the cast castclass [mscorlib]System.String with nop and recompile with ilasm.exe.

Now the call to SetValue1 with wrong type argument passes and produces the same result as your dynamic method. So it would seem that CLR is not doing type checking in this case. The documentation says:

During just-in-time (JIT) compilation, an optional verification process examines the metadata and Microsoft intermediate language (MSIL) of a method to be JIT-compiled into native machine code to verify that they are type safe. This process is skipped if the code has permission to bypass verification.

In this case, we are running code on local machine, so the CLR trusts that the IL is valid.

You can manually verify the assembyl by running peverify.exe on the output .exe file. It will return with an error: Program::SetValue1][offset 0x00000004][found ref 'System.Object'][expected ref 'System.String'] Unexpected type on the stack.

There is a very good post that explores this topic: http://www.pcreview.co.uk/forums/net-type-safety-and-net-configuration-tool-t1225543.html

like image 116
qbik Avatar answered Oct 19 '22 22:10

qbik