lundi 14 mars 2016

Access automatically created anonymus type fields

Let us imaging we emit a class representing circle. We define a double property representing its radius, with correlating backing field and get/set accessors. Then we prepare a logic of computing area, in a form of anonymous method, using newly created PropertyBuilder.

var assemblyName = new AssemblyName();
assemblyName.Name = "SampleAssembly";
var domain = Thread.GetDomain();
var assembly = domain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave);
var module = assembly.DefineDynamicModule(assemblyName.Name, assemblyName.Name + ".dll", true);
var baseType = typeof(object);
var type = module.DefineType("Circle", TypeAttributes.Public, baseType);
var propertyType = typeof(double);
var radiusProperty = type.DefineProperty("Radius", PropertyAttributes.None, propertyType, null);
var backingField = type.DefineField("<Radius>k__BackingField", propertyType, FieldAttributes.Private);
var getter = type.DefineMethod("get_Radius", MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName, propertyType, null);
var getterIl = getter.GetILGenerator();
getterIl.Emit(OpCodes.Ldarg_0);
getterIl.Emit(OpCodes.Ldfld, backingField);
getterIl.Emit(OpCodes.Ret);
radiusProperty.SetGetMethod(getter);
var setter = type.DefineMethod("set_Radius", MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.SpecialName, null, new Type[] { propertyType });
var valueParameter = setter.DefineParameter(1, ParameterAttributes.None, "value");
var setterIl = setter.GetILGenerator();
setterIl.Emit(OpCodes.Ldarg_0);
setterIl.Emit(OpCodes.Ldarg_1);
setterIl.Emit(OpCodes.Stfld, backingField);
setterIl.Emit(OpCodes.Ret);
radiusProperty.SetSetMethod(setter);
Func<double> circleAreaExpression = () => Math.PI * Math.Pow((double)radiusProperty.GetValue(this), 2);
var or = new OperationReader(circleAreaExpression.Method);
var frame = new StackFrame();
var body = frame.GetMethod().GetMethodBody();
var locals = body.LocalVariables;

Inspecting intermediate language of the anonymous method shows that it contains Load Field radiusProperty operation, where radiusProperty is our newly created PropertyBuilder, preserved as a field of some anonymous class.

or.Operations {System.Reflection.Operation[11]} System.Reflection.Operation[] + [0] {0000 : ldc.r81414344187} System.Reflection.Operation + [1] {0009 : ldarg.0} System.Reflection.Operation + [2] {0010 : ldfld System.Reflection.Emit.PropertyBuilder MethodTest.Program+<>c__DisplayClass0_0::radiusProperty}
System.Reflection.Operation + [3] {0015 : ldarg.0} System.Reflection.Operation + [4] {0016 : ldfld MethodTest.Program MethodTest.Program+<>c__DisplayClass0_0::<>4__this} System.Reflection.Operation + [5] {0021 : callvirt instance System.Object System.Reflection.PropertyInfo::GetValue()}
System.Reflection.Operation + [6] {0026 : unbox.any System.Double} System.Reflection.Operation + [7] {0031 : ldc.r81073741824} System.Reflection.Operation + [8] {0040 : call System.Double System.Math::Pow()} System.Reflection.Operation + [9] {0045 : mul} System.Reflection.Operation + [10] {0046 : ret} System.Reflection.Operation

What we would like to achieve now, is get our original PropertyBuilder, that it is a field of mentioned anonymous class. Browsing local variables of the method body that we are currently in shows, that the interesting anonymous class proudly holds the first position in local variables array. Although we do not have any reference to that anonymous class instance. Any tips how to get reference to the interesting PropertyBuilder?

locals Count = 19 System.Collections.Generic.IList {System.Collections.ObjectModel.ReadOnlyCollection} + [0] {MethodTest.Program+<>c__DisplayClass0_0 (0)}
System.Reflection.LocalVariableInfo + [1] {System.Reflection.AssemblyName (1)}
System.Reflection.LocalVariableInfo + [2] {System.AppDomain (2)} System.Reflection.LocalVariableInfo + [3] {System.Reflection.Emit.AssemblyBuilder (3)} System.Reflection.LocalVariableInfo + [4] {System.Reflection.Emit.ModuleBuilder (4)}
System.Reflection.LocalVariableInfo + [5] {System.Type (5)} System.Reflection.LocalVariableInfo + [6] {System.Reflection.Emit.TypeBuilder (6)}
System.Reflection.LocalVariableInfo + [7] {System.Type (7)} System.Reflection.LocalVariableInfo + [8] {System.Reflection.Emit.FieldBuilder (8)}
System.Reflection.LocalVariableInfo + [9] {System.Reflection.Emit.MethodBuilder (9)}
System.Reflection.LocalVariableInfo + [10] {System.Reflection.Emit.ILGenerator (10)}
System.Reflection.LocalVariableInfo + [11] {System.Reflection.Emit.MethodBuilder (11)} System.Reflection.LocalVariableInfo + [12] {System.Reflection.Emit.ParameterBuilder (12)}
System.Reflection.LocalVariableInfo + [13] {System.Reflection.Emit.ILGenerator (13)}
System.Reflection.LocalVariableInfo + [14] {System.Func``1[System.Double] (14)} System.Reflection.LocalVariableInfo + [15] {System.Reflection.OperationReader (15)}
System.Reflection.LocalVariableInfo + [16] {System.Diagnostics.StackFrame (16)} System.Reflection.LocalVariableInfo + [17] {System.Reflection.MethodBody (17)} System.Reflection.LocalVariableInfo + [18] {System.Collections.Generic.IList`1[System.Reflection.LocalVariableInfo] (18)} System.Reflection.LocalVariableInfo

Here are missing classes : link1 link2





Aucun commentaire:

Enregistrer un commentaire