private static void EmitReadMethodContents( ILGenerator gen, Type formattedType, FieldInfo dictField, Dictionary <Type, FieldBuilder> serializerFields, Dictionary <MemberInfo, List <string> > memberNames, Dictionary <Type, MethodInfo> serializerReadMethods) { MethodInfo skipMethod = typeof(IDataReader).GetMethod("SkipEntry", Flags.InstancePublic); MethodInfo tryGetValueMethod = typeof(Dictionary <string, int>).GetMethod("TryGetValue", Flags.InstancePublic); //methodBuilder.DefineParameter(5, ParameterAttributes.None, "switchLookup"); LocalBuilder lookupResult = gen.DeclareLocal(typeof(int)); Label defaultLabel = gen.DefineLabel(); Label switchLabel = gen.DefineLabel(); Label endLabel = gen.DefineLabel(); Label[] switchLabels = memberNames.Select(n => gen.DefineLabel()).ToArray(); gen.Emit(OpCodes.Ldarg_1); // Load entryName string gen.Emit(OpCodes.Ldnull); // Load null gen.Emit(OpCodes.Ceq); // Equality check gen.Emit(OpCodes.Brtrue, defaultLabel); // If entryName is null, go to default case //gen.Emit(OpCodes.Ldarg, (short)4); // Load lookup dictionary argument (OLD CODE) gen.Emit(OpCodes.Ldsfld, dictField); // Load lookup dictionary from static field on helper type gen.Emit(OpCodes.Ldarg_1); // Load entryName string gen.Emit(OpCodes.Ldloca, (short)lookupResult.LocalIndex); // Load address of lookupResult gen.Emit(OpCodes.Callvirt, tryGetValueMethod); // Call TryGetValue on the dictionary gen.Emit(OpCodes.Brtrue, switchLabel); // If TryGetValue returned true, go to the switch case gen.Emit(OpCodes.Br, defaultLabel); // Else, go to the default case gen.MarkLabel(switchLabel); // Switch starts here gen.Emit(OpCodes.Ldloc, lookupResult); // Load lookupResult gen.Emit(OpCodes.Switch, switchLabels); // Perform switch on switchLabels int count = 0; foreach (var member in memberNames.Keys) { var memberType = FormatterUtilities.GetContainedType(member); var propInfo = member as PropertyInfo; var fieldInfo = member as FieldInfo; gen.MarkLabel(switchLabels[count]); // Switch case for [count] starts here // Now we load the instance that we have to set the value on gen.Emit(OpCodes.Ldarg_0); // Load value reference if (formattedType.IsValueType == false) { gen.Emit(OpCodes.Ldind_Ref); // Indirectly load value of reference } // Now we deserialize the value itself gen.Emit(OpCodes.Ldsfld, serializerFields[memberType]); // Load serializer from serializer container type gen.Emit(OpCodes.Ldarg, (short)3); // Load reader argument gen.Emit(OpCodes.Callvirt, serializerReadMethods[memberType]); // Call Serializer.ReadValue(IDataReader reader) // The stack now contains the formatted instance and the deserialized value to set the member to // Now we set the value if (fieldInfo != null) { gen.Emit(OpCodes.Stfld, fieldInfo.DeAliasField()); // Set field } else if (propInfo != null) { gen.Emit(OpCodes.Callvirt, propInfo.DeAliasProperty().GetSetMethod(true)); // Call property setter } else { throw new NotImplementedException(); } gen.Emit(OpCodes.Br, endLabel); // Jump to end of method count++; } gen.MarkLabel(defaultLabel); // Default case starts here gen.Emit(OpCodes.Ldarg, (short)3); // Load reader argument gen.Emit(OpCodes.Callvirt, skipMethod); // Call IDataReader.SkipEntry gen.MarkLabel(endLabel); // Method end starts here gen.Emit(OpCodes.Ret); // Return method }
private static Type BuildHelperType( ModuleBuilder moduleBuilder, string helperTypeName, Type formattedType, Dictionary <string, MemberInfo> serializableMembers, out Dictionary <Type, MethodInfo> serializerReadMethods, out Dictionary <Type, MethodInfo> serializerWriteMethods, out Dictionary <Type, FieldBuilder> serializerFields, out FieldBuilder dictField, out Dictionary <MemberInfo, List <string> > memberNames) { TypeBuilder helperTypeBuilder = moduleBuilder.DefineType(helperTypeName, TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.Class); memberNames = new Dictionary <MemberInfo, List <string> >(); foreach (var entry in serializableMembers) { List <string> list; if (memberNames.TryGetValue(entry.Value, out list) == false) { list = new List <string>(); memberNames.Add(entry.Value, list); } list.Add(entry.Key); } dictField = helperTypeBuilder.DefineField("SwitchLookup", typeof(Dictionary <string, int>), FieldAttributes.Public | FieldAttributes.Static | FieldAttributes.InitOnly); List <Type> neededSerializers = memberNames.Keys.Select(n => FormatterUtilities.GetContainedType(n)).Distinct().ToList(); serializerReadMethods = new Dictionary <Type, MethodInfo>(neededSerializers.Count); serializerWriteMethods = new Dictionary <Type, MethodInfo>(neededSerializers.Count); serializerFields = new Dictionary <Type, FieldBuilder>(neededSerializers.Count); foreach (var t in neededSerializers) { string name = t.GetCompilableNiceFullName() + "__Serializer"; int counter = 1; while (serializerFields.Values.Any(n => n.Name == name)) { counter++; name = t.GetCompilableNiceFullName() + "__Serializer" + counter; } Type serializerType = typeof(Serializer <>).MakeGenericType(t); serializerReadMethods.Add(t, serializerType.GetMethod("ReadValue", Flags.InstancePublicDeclaredOnly)); serializerWriteMethods.Add(t, serializerType.GetMethod("WriteValue", Flags.InstancePublicDeclaredOnly, null, new[] { typeof(string), t, typeof(IDataWriter) }, null)); serializerFields.Add(t, helperTypeBuilder.DefineField(name, serializerType, FieldAttributes.Public | FieldAttributes.Static | FieldAttributes.InitOnly)); } //FieldBuilder readMethodFieldBuilder = helperTypeBuilder.DefineField("ReadMethod", readDelegateType, FieldAttributes.Public | FieldAttributes.Static | FieldAttributes.InitOnly); //FieldBuilder writeMethodFieldBuilder = helperTypeBuilder.DefineField("WriteMethod", writeDelegateType, FieldAttributes.Public | FieldAttributes.Static | FieldAttributes.InitOnly); // We generate a static constructor for our formatter helper type that initializes our switch lookup dictionary and our needed Serializer references { var addMethod = typeof(Dictionary <string, int>).GetMethod("Add", Flags.InstancePublic); var dictionaryConstructor = typeof(Dictionary <string, int>).GetConstructor(Type.EmptyTypes); var serializerGetMethod = typeof(Serializer).GetMethod("Get", Flags.StaticPublic, null, new[] { typeof(Type) }, null); var typeOfMethod = typeof(Type).GetMethod("GetTypeFromHandle", Flags.StaticPublic, null, new Type[] { typeof(RuntimeTypeHandle) }, null); ConstructorBuilder staticConstructor = helperTypeBuilder.DefineTypeInitializer(); ILGenerator gen = staticConstructor.GetILGenerator(); gen.Emit(OpCodes.Newobj, dictionaryConstructor); // Create new dictionary int count = 0; foreach (var entry in memberNames) { foreach (var name in entry.Value) { gen.Emit(OpCodes.Dup); // Load duplicate dictionary value gen.Emit(OpCodes.Ldstr, name); // Load entry name gen.Emit(OpCodes.Ldc_I4, count); // Load entry index gen.Emit(OpCodes.Call, addMethod); // Call dictionary add } count++; } gen.Emit(OpCodes.Stsfld, dictField); // Set static dictionary field to dictionary value foreach (var entry in serializerFields) { gen.Emit(OpCodes.Ldtoken, entry.Key); // Load type token gen.Emit(OpCodes.Call, typeOfMethod); // Call typeof method (this pushes a type value onto the stack) gen.Emit(OpCodes.Call, serializerGetMethod); // Call Serializer.Get(Type type) method gen.Emit(OpCodes.Stsfld, entry.Value); // Set static serializer field to result of get method } gen.Emit(OpCodes.Ret); // Return } // Now we need to actually create the serializer container type so we can generate the dynamic methods below without getting TypeLoadExceptions up the wazoo return(helperTypeBuilder.CreateType()); }