Home > other >  Common Language Runtime detected an invalid program in DateTime getter
Common Language Runtime detected an invalid program in DateTime getter

Time:12-24

I'm making a class for generating wrappers in runtime, that get a class like this:

class Test
{
    public int A { get; set; }

    public int B { get; set; }

    public string C { get; set; }

    public DateTimeOffset D { get; set; }
    
    public bool E { get; set; }
}

and creates a wrapper type like

class newTest
{
    private Test _inner;

    public newTest(Test inner)
    {
        _inner = inner;
    }
    
    public int A { get => _inner.A; }

    public int B { get => _inner.B; }

    public string C
    {
        get => _inner.C;
    }

    public DateTime D
    {
        get => _inner.D.UtcDateTime;
    }
    
    public short E
    {
        get => (short)(_inner.E ? 1 : 0);
    }
}

and all works fine, except getter for property D (only the DateTime part in utc timezone is needed). Code for adding property for DateTimeOffset is

        private static void AddDateTimeOffsetProperty(TypeBuilder typeBuilder, FieldInfo fieldBuilder,
        PropertyInfo field)
    {
        var propBuilder = typeBuilder.DefineProperty(field.Name
            , PropertyAttributes.HasDefault
            , typeof(DateTime)
            , null);

        var getBuilder = typeBuilder.DefineMethod($"get_{field.Name}",
            _getAttr,
            typeof(DateTime),
            Type.EmptyTypes);

        ILGenerator ilGen = getBuilder.GetILGenerator();
        // load args to memory
        ilGen.Emit(OpCodes.Ldarg_0);
        // load required field from "_inner" obj
        ilGen.Emit(OpCodes.Ldfld, fieldBuilder);
        // call _inner getter
        ilGen.EmitCall(OpCodes.Callvirt, field.GetMethod!, Array.Empty<Type>());

        // alloc local variable
        ilGen.Emit(OpCodes.Stloc_0);
        ilGen.Emit(OpCodes.Ldloca_S);

        MethodInfo utcDateTimeMethod = typeof(DateTimeOffset).GetProperty("UtcDateTime").GetMethod;
        // get UtcDateTime
        ilGen.EmitCall(OpCodes.Call, utcDateTimeMethod!, Array.Empty<Type>());
        // return
        ilGen.Emit(OpCodes.Ret);

        propBuilder.SetGetMethod(getBuilder);
    }

Here "field" is the property of inner class. After class was generated, create instance of Test class and wrap it

                var testObj = new Test
        {
            A = 7,
            B = 6,
            C = "World",
            E = false,
            D = DateTimeOffset.UtcNow.ToOffset(TimeSpan.FromHours(7)),
            F = 14
        };

        var wrapper = Activator.CreateInstance(wrapperTypeForTest, testObj);

after that wrapper.D will return "Common Language Runtime detected an invalid program.", other properties work.

CodePudding user response:

You're storing something in slot 0 (with Stloc_0), but you never declared slot 0.

You need to declare a local first, so ILGenerator can actually create the slot to store your variable in.

LocalBuilder local = ilGen.DeclareLocal(typeof(DateTimeOffset));

And then:

ilGen.Emit(OpCodes.Stloc, local);

You're using Ldloca_S to load the address of a local slot, but you never pass the slot to actually load.

ilGen.Emit(OpCodes.Ldloca, local);

(ILGenerator will turn this into Ldloca_S if appropriate).


From the docs for ILGenerator.EmitCall:

Puts a call or callvirt instruction onto the Microsoft intermediate language (MSIL) stream to call a varargs method.

You're not calling a varargs method. Varargs methods are few and far between. You're calling a normal method, so just:

ilGen.Emit(OpCodes.Callvirt, field.GetMethod!);

and:

ilGen.Emit(OpCodes.Call, utcDateTimeMethod!);
  • Related