예제 #1
0
        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
        }
예제 #2
0
        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());
        }