Vitaliy Kalinin
Vitaliy Kalinin

Reputation: 1871

Generate function which return property value of property

I am trying to emit following:

This is struct which will be included as property to a dynamically emited class:

public struct GeoPoint
{
    public double? lat { get; set; }

    public double? lon { get; set; }
}

I have to emit following class:

public class GeoPointOwner
{
    public GeoPoint GeoPoint{get; set;}
    public double? Getlat ()
    {
        return GeoPoint.lat;
    }
}

I can't figure out how properly emit method GeoPointOwner.Getlat

Here is my code:

var mbGet = typeBuilder.DefineMethod("Getlat", MethodAttributes.Public, typeof(double?), Type.EmptyTypes);
var genGet = mbGet.GetILGenerator();
genGet.Emit(OpCodes.Ldarg_0);
//getMethodInfo below is MethodInfo of Getter of GeoPoint property
genGet.Emit(OpCodes.Call, getMethodInfo);
var getMethod = typeof(GeoPoint).GetProperty("lat").GetGetMethod(true);
//code emitted below fails when invoked
genGet.Emit(OpCodes.Callvirt, getMethod);
genGet.Emit(OpCodes.Stloc_0);
genGet.Emit(OpCodes.Ret);

Upvotes: 0

Views: 139

Answers (1)

svick
svick

Reputation: 244837

As I suggested in a comment, with errors like this, it's best to use PEVerify on the generated assembly. Using that on your code will help you fix it.

If I run it on the generated assembly, I get this error:

[GeoPointOwner::Getlat][offset 0x00000006] Callvirt on a value type method.

You can't use callvirt to call a method on a value type (in your case, that's the getter for GeoPoint.lat), use call instead.

After fixing that, I get two new errors:

[GeoPointOwner::Getlat][offset 0x00000006][found value 'ConsoleApplication1.GeoPoint'][expected address of value 'ConsoleApplication1.GeoPoint'] Unexpected type on the stack.

[GeoPointOwner::Getlat][offset 0x0000000B] Unrecognized local variable number.

I'm going to first fix the second one. You're using stloc_0, which stores the value on the top of the stack to the local variable #0. It fails, because you didn't declare any locals. I don't understand why are you doing that, since you don't need any locals here. You can directly return the result of the call, so you can remove the stloc_0.

The first error is more complicated. When you want to call a method on a value type, you actually need a reference to a variable of that type. We don't have a variable right now, which means we have to create one (using DeclareLocal()), save the value to a local (stloc) and then load the address of that local (ldloca). For some more information, see the question Method invocation on a struct?.

With those changes, your code becomes something like:

var getlatBuilder = type.DefineMethod("Getlat", MethodAttributes.Public, typeof(double?), Type.EmptyTypes);
var il = getlatBuilder.GetILGenerator();
var getPointLocal = il.DeclareLocal(typeof(GeoPoint));

il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Call, geoPointGetter);

il.Emit(OpCodes.Stloc, getPointLocal);
il.Emit(OpCodes.Ldloca, getPointLocal);

var latGetter = typeof(GeoPoint).GetProperty("lat").GetGetMethod(true);
il.Emit(OpCodes.Call, latGetter);
il.Emit(OpCodes.Ret);

Upvotes: 2

Related Questions