ILGenerator property not an instance

382 views Asked by At

I'd like to get this:

.property instance class [WorldTool.Core]WorldTool.IInputPort SomePort
{
    .get instance class [WorldTool.Core]WorldTool.IInputPort WorldTool.Core.Tests.SomeOperatorInstance::get_SomePort()
}

But I'm getting this:

.property class class [WorldTool.Core]WorldTool.IInputPort SomePort
{
    .get instance class [WorldTool.Core]WorldTool.IInputPort SomeOperatorInstanceProxy::get_SomePort()
}

Why am I getting ".property class class" and not ".property instance class"? Here's what I'm using to generate it:

private void CreateProperty(TypeBuilder typeBuilder, PropertyInfo propertyInfo)
{
    var propertyBuilder = typeBuilder.DefineProperty(propertyInfo.Name,
                                                        PropertyAttributes.HasDefault,  
                                                        propertyInfo.PropertyType,
                                                        Type.EmptyTypes);

    var methodBuilder = typeBuilder.DefineMethod(GET_PREFIX + propertyInfo.Name,
                                                    MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.NewSlot | MethodAttributes.Virtual | MethodAttributes.Final,
                                                    propertyInfo.PropertyType, 
                                                    Type.EmptyTypes);

    var methodGenerator = methodBuilder.GetILGenerator();
    methodGenerator.DeclareLocal(propertyInfo.PropertyType);
    methodGenerator.Emit(OpCodes.Nop);
    methodGenerator.Emit(OpCodes.Ldarg_0);
    methodGenerator.Emit(OpCodes.Ldstr, propertyInfo.Name);
    methodGenerator.Emit(OpCodes.Call, typeof(IInputPort).IsAssignableFrom(propertyInfo.PropertyType) ? GetNamedInputPort : GetNamedOutputPort);
    methodGenerator.Emit(OpCodes.Stloc_0);

    var targetInstruction = methodGenerator.DefineLabel();
    methodGenerator.Emit(OpCodes.Br_S, targetInstruction);

    methodGenerator.MarkLabel(targetInstruction);
    methodGenerator.Emit(OpCodes.Ldloc_0);

    methodGenerator.Emit(OpCodes.Ret);

    propertyBuilder.SetGetMethod(methodBuilder);
}
1

There are 1 answers

2
antiduh On BEST ANSWER

You need to specify the calling conventions when defining the property.

You have:

var propertyBuilder = typeBuilder.DefineProperty(
     propertyInfo.Name,
     PropertyAttributes.HasDefault,  
     propertyInfo.PropertyType,
     Type.EmptyTypes);

You need:

var propertyBuilder = typeBuilder.DefineProperty(
     propertyInfo.Name,
     PropertyAttributes.HasDefault,  
     CallingConventions.HasThis, // ding ding ding
     propertyInfo.PropertyType,
     Type.EmptyTypes);

And to anybody wishing to repeat my findings, here's the test code I was using:

namespace EmitTest
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Reflection.Emit;
    using System.Reflection;

    public class TestPropertyBuilde
    {
        TypeBuilder tb;
        FieldBuilder countFB;

        // The code in this class generates the code in the following comment:
        /*
        namespace EmitTest
        {
            public class TestType
            {
                private int count;
                public int TestProperty
                {
                    get
                    {
                        return this.count;
                    }
                }
                public TestType()
                {
                    this.count = 1;
                }
            }
        }
        */

        public void Build()
        {
            AssemblyName name = new AssemblyName( "GeneratedAssm" );

            AssemblyBuilder assembly = AppDomain.CurrentDomain.DefineDynamicAssembly( 
                name, 
                AssemblyBuilderAccess.RunAndSave, 
                @"C:\Users\antiduh\Desktop\EmitTest" );

            ModuleBuilder mb = assembly.DefineDynamicModule( name.Name, name.Name + ".dll" );

            this.tb = mb.DefineType( "EmitTest.TestType", TypeAttributes.Public );

            CreateVariable();

            CreateConstructor();

            CreateProperty( "TestProperty", typeof( int ) );

            Type dummy = tb.CreateType();
            assembly.Save( "GeneratedAssm.dll" );
        }

        private void CreateVariable()
        {
            this.countFB = tb.DefineField( "count", typeof( int ), FieldAttributes.Private );
        }

        private void CreateConstructor()
        {
            ConstructorBuilder ctor0 = tb.DefineConstructor(
                MethodAttributes.Public,
                CallingConventions.Standard,
                Type.EmptyTypes );


            var gen = ctor0.GetILGenerator();

            gen.Emit( OpCodes.Ldarg_0 );
            gen.Emit( OpCodes.Call, typeof( object ).GetConstructor( Type.EmptyTypes ) );

            gen.Emit( OpCodes.Ldarg_0 );
            gen.Emit( OpCodes.Ldc_I4_1 );
            gen.Emit( OpCodes.Stfld, countFB);

            gen.Emit( OpCodes.Ret );
        }

        private void CreateProperty(string propertyName, Type propType )
        {

            var propertyBuilder = tb.DefineProperty( 
                propertyName, 
                PropertyAttributes.HasDefault,
                CallingConventions.HasThis, 
                propType, 
                Type.EmptyTypes );

            var methodBuilder = tb.DefineMethod( 
                "get_" + propertyName,
                MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.NewSlot | MethodAttributes.Virtual | MethodAttributes.Final,
                propType,
                Type.EmptyTypes );

            var methodGenerator = methodBuilder.GetILGenerator();

            methodGenerator.Emit( OpCodes.Ldarg_0 );
            methodGenerator.Emit( OpCodes.Ldfld, countFB );
            methodGenerator.Emit( OpCodes.Ret );

        propertyBuilder.SetGetMethod( methodBuilder );
    }
}

}