Search code examples
c#cilgenerated-code

IL emit with base class method invocation?


How do I modify this IL-emit-code to include calling a method in a base class in the getter and setter? Whatever I do I get InvalidProgramException.

FieldBuilder fieldBuilder = tb.DefineField("_" + propertyName, propertyType, FieldAttributes.Private);

PropertyBuilder propertyBuilder = tb.DefineProperty(propertyName, PropertyAttributes.HasDefault, propertyType, null);
MethodBuilder getPropMthdBldr = tb.DefineMethod("get_" + propertyName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, propertyType, Type.EmptyTypes);
ILGenerator getIl = getPropMthdBldr.GetILGenerator();

getIl.Emit(OpCodes.Ldarg_0);

//getIl.Emit(OpCodes.Call, baseType.GetMethod("Test"));

getIl.Emit(OpCodes.Ldfld, fieldBuilder);
getIl.Emit(OpCodes.Ret);

MethodBuilder setPropMthdBldr =
    tb.DefineMethod("set_" + propertyName,
      MethodAttributes.Public |
      MethodAttributes.SpecialName |
      MethodAttributes.HideBySig,
      null, new[] { propertyType });

ILGenerator setIl = setPropMthdBldr.GetILGenerator();

Label modifyProperty = setIl.DefineLabel();
Label exitSet = setIl.DefineLabel();

setIl.MarkLabel(modifyProperty);

//setIl.Emit(OpCodes.Call, baseType.GetMethod("Test"));

setIl.Emit(OpCodes.Ldarg_0);
setIl.Emit(OpCodes.Ldarg_1);
setIl.Emit(OpCodes.Stfld, fieldBuilder);

setIl.Emit(OpCodes.Nop);
setIl.MarkLabel(exitSet);
setIl.Emit(OpCodes.Ret);

propertyBuilder.SetGetMethod(getPropMthdBldr);
propertyBuilder.SetSetMethod(setPropMthdBldr);

Solution

  • You are required to have the instance on the evaluation stack when calling an instance method. Since you are emitting an instance property, arg 0 has a reference to the instance. Emit an additional ldarg0 instruction before the call instruction. If the method has non-void return type you will will also need to emit a pop instruction to remove the return value from the stack or use it in some other instruction.