private static Dictionary <string, BinField> GetFieldsMap(TypeImpl type) =>
 BinField.Get(type.Type).ToDictionary(GetFieldId, GetField);
        private static void GenerateObjectReader(ILGenerator il, TypeImpl type)
        {
            var addRef = typeof(RefReaderWatcher)
                         .GetTypeInfo()
                         .GetMethod(nameof(RefReaderWatcher.AddRef), BindingFlags.Public | BindingFlags.Static)
                         .MakeGenericMethod(type.Type);

            var tryGetRef = typeof(RefReaderWatcher)
                            .GetTypeInfo()
                            .GetMethod(nameof(RefReaderWatcher.TryGetRef), BindingFlags.Public | BindingFlags.Static)
                            .MakeGenericMethod(type.Type);

            var getTypeFromHandle = typeof(Type)
                                    .GetTypeInfo()
                                    .GetMethod(nameof(Type.GetTypeFromHandle), BindingFlags.Public | BindingFlags.Static);

            var defaultCtor = type.TypeInfo.GetConstructor(Type.EmptyTypes);

            var getUninitializedObject = typeof(FormatterServices)
                                         .GetTypeInfo()
                                         .GetMethod(nameof(FormatterServices.GetUninitializedObject), BindingFlags.Public | BindingFlags.Static);

            var dynamicObjectRead = typeof(DynamicObjectReader <>)
                                    .MakeGenericType(type.Type)
                                    .GetTypeInfo()
                                    .GetMethod(nameof(DynamicObjectReader <object> .Read), BindingFlags.Public | BindingFlags.Static);

            var stringEquals = typeof(string)
                               .GetTypeInfo()
                               .GetMethod(nameof(string.Equals), new[] { typeof(string), typeof(string), typeof(StringComparison) });

            var onDeserialized = typeof(IBinSerializable).GetMethod(nameof(IBinSerializable.OnDeserialized));


            il.DeclareLocal(typeof(int));                 // ref if
            il.DeclareLocal(type.Type);                   // readed object
            il.DeclareLocal(typeof(string));              // last readed field id
            il.DeclareLocal(typeof(int));                 // readed object version
            il.DeclareLocal(typeof(DeserializationInfo)); // info

            var resultLabel   = il.DefineLabel();
            var readFastLabel = il.DefineLabel();

            // Process reference id
            if (!type.TypeInfo.IsValueType)
            {
                var tryLoadReferenceLabel = il.DefineLabel();

                // Read reference id
                il.Emit(OpCodes.Ldarg_0);                                               // Load stream
                il.Emit(OpCodes.Call, SerializerTypes.TryGetStreamReader(typeof(int))); // Read ref id
                il.Emit(OpCodes.Dup);                                                   // Duplicate ref id
                il.Emit(OpCodes.Stloc_0);                                               // Set ref id to local

                // Check if result null
                il.Emit(OpCodes.Brtrue, tryLoadReferenceLabel); // Check if null was written

                // Null was written (return null)
                il.Emit(OpCodes.Ldnull);
                il.Emit(OpCodes.Stloc_1);         // Set result to null
                il.Emit(OpCodes.Br, resultLabel); // Jump to result

                // Try get readed reference
                il.MarkLabel(tryLoadReferenceLabel);
                il.Emit(OpCodes.Ldloc_0);             // Load reference id
                il.Emit(OpCodes.Ldloca_S, (byte)1);   // Load address of result
                il.Emit(OpCodes.Call, tryGetRef);
                il.Emit(OpCodes.Brtrue, resultLabel); // Jump to result if reference already exist
            }

            // Read version
            il.Emit(OpCodes.Ldarg_0);                                               // Load stream
            il.Emit(OpCodes.Call, SerializerTypes.TryGetStreamReader(typeof(int))); // Read saved version
            il.Emit(OpCodes.Stloc_3);                                               // Save version to local

            var reader = SerializerTypes.TryGetStreamReader(type);

            if (reader != null)
            {
                // Read
                il.Emit(OpCodes.Ldarg_0); // Load stream
                il.Emit(OpCodes.Call, reader);
                il.Emit(OpCodes.Stloc_1); // Set result to local

                // Add reference to watcher
                if (!type.TypeInfo.IsValueType)
                {
                    il.Emit(OpCodes.Ldloc_0); // Load reference id
                    il.Emit(OpCodes.Ldloc_1); // Load result object reference
                    il.Emit(OpCodes.Call, addRef);
                }
            }
            else
            {
                // Create
                if (!type.TypeInfo.IsValueType)
                {
                    if (defaultCtor != null)
                    {
                        il.Emit(OpCodes.Newobj, defaultCtor);
                        il.Emit(OpCodes.Stloc_1); // Set result to local
                    }
                    else
                    {
                        il.Emit(OpCodes.Ldtoken, type.Type);
                        il.Emit(OpCodes.Call, getTypeFromHandle);
                        il.Emit(OpCodes.Call, getUninitializedObject);
                        il.Emit(OpCodes.Castclass, type.Type);
                        il.Emit(OpCodes.Stloc_1); // Set result to local
                    }
                }
                else
                {
                    il.Emit(OpCodes.Ldloca_S, (byte)1); // Load result local address
                    il.Emit(OpCodes.Initobj, type.Type);
                }

                // Add reference to watcher
                if (!type.TypeInfo.IsValueType)
                {
                    il.Emit(OpCodes.Ldloc_0); // Load reference id
                    il.Emit(OpCodes.Ldloc_1); // Load result object reference
                    il.Emit(OpCodes.Call, addRef);
                }

                // Read code type version
                var version = SerializerTypes.GetVersion(type);

                il.Emit(OpCodes.Ldloc_3);         // Load version
                il.Emit(OpCodes.Ldc_I4, version); // Load program type version
                il.Emit(OpCodes.Beq, readFastLabel);

                // Call dynamic read if versions not equal
                il.Emit(OpCodes.Ldarg_0);                 // Load stream
                il.Emit(OpCodes.Ldloc_1);                 // Load obj instance
                il.Emit(OpCodes.Ldloc_3);                 // Load version
                il.Emit(OpCodes.Call, dynamicObjectRead); // Do dynamic read
                il.Emit(OpCodes.Stloc_1);                 // Set readed value to result local
                il.Emit(OpCodes.Br, resultLabel);         // Jump to return code part

                il.MarkLabel(readFastLabel);

                // Read
                var typeReader = SerializerTypes.TryGetTypeReader(type);
                if (typeReader != null)
                {
                    // Read type
                    il.Emit(OpCodes.Ldarg_0); // Load stream
                    il.Emit(OpCodes.Ldloc_1); // Load obj instance
                    il.Emit(OpCodes.Ldloc_3); // Load version
                    il.Emit(OpCodes.Call, typeReader);
                    il.Emit(OpCodes.Stloc_1); // Set result to local
                }
                else
                {
                    // Read fields
                    Label?nextFieldLabel = null;

                    foreach (var binField in BinField.Get(type.Type))
                    {
                        var markCurrent = nextFieldLabel != null;
                        if (nextFieldLabel == null)
                        {
                            nextFieldLabel = il.DefineLabel();
                        }

                        // Read field id
                        il.Emit(OpCodes.Ldarg_0);                                                  // Load stream
                        il.Emit(OpCodes.Call, SerializerTypes.TryGetStreamReader(typeof(string))); // Read field id
                        il.Emit(OpCodes.Stloc_2);                                                  // Save last readed field id

                        // Mark next field label only after field read
                        if (markCurrent)
                        {
                            il.MarkLabel(nextFieldLabel.Value);
                            nextFieldLabel = il.DefineLabel();
                        }

                        // Compare field id with endToken
                        il.Emit(OpCodes.Ldloc_2);                               // Load readed field id
                        il.Emit(OpCodes.Ldstr, SerializerTypes.TypeEndToken);   // Load endToken id
                        il.Emit(OpCodes.Ldc_I4, (int)StringComparison.Ordinal); // Load comparsion type
                        il.Emit(OpCodes.Call, stringEquals);                    // Compare
                        il.Emit(OpCodes.Brtrue, resultLabel);                   // This is the end

                        // Compare field ids, if it not equals then skip read
                        il.Emit(OpCodes.Ldloc_2);                               // Load readed field id
                        il.Emit(OpCodes.Ldstr, binField.Id);                    // Load field id
                        il.Emit(OpCodes.Ldc_I4, (int)StringComparison.Ordinal); // Load comparsion type
                        il.Emit(OpCodes.Call, stringEquals);                    // Compare
                        il.Emit(OpCodes.Brfalse, nextFieldLabel.Value);         // These aren't the field your looking for

                        var deserialize = typeof(BinSerializer <>)
                                          .MakeGenericType(binField.Type)
                                          .GetTypeInfo()
                                          .GetMethod(nameof(BinSerializer <object> .Deserialize), BindingFlags.Static | BindingFlags.Public);

                        // Prepeare stack to field set
                        if (type.TypeInfo.IsValueType)
                        {
                            il.Emit(OpCodes.Ldloca_S, (byte)1);
                        }
                        else
                        {
                            il.Emit(OpCodes.Ldloc_1);
                        }

                        // Read field
                        il.Emit(OpCodes.Ldarg_0); // Load stream
                        il.Emit(OpCodes.Call, deserialize);

                        // Set field
                        binField.EmitWrite(il);
                    }

                    // Mark jump (for last field)
                    if (nextFieldLabel != null)
                    {
                        il.MarkLabel(nextFieldLabel.Value);
                    }

                    // Skip end
                    il.Emit(OpCodes.Ldarg_0);
                    il.Emit(OpCodes.Call, SerializerTypes.TryGetStreamSkiper(typeof(string)));
                }
            }

            // Return result and invoke callback
            il.MarkLabel(resultLabel);

            // Invoke deserialzation callback
            if (typeof(IBinSerializable).IsAssignableFrom(type.Type))
            {
                il.Emit(OpCodes.Ldloca_S, (byte)4); // Load result local address
                il.Emit(OpCodes.Ldloc_3);           // Load version
                il.Emit(OpCodes.Call, typeof(DeserializationInfo).GetConstructor(new Type[] { typeof(int) }));

                il.Emit(OpCodes.Ldloc_1);                  // Load deserialized object
                il.Emit(OpCodes.Ldloc_S, (byte)4);         // Load created DeserialzationInfo
                il.Emit(OpCodes.Callvirt, onDeserialized); // Call onDeserialized
            }

            // Return result
            il.Emit(OpCodes.Ldloc_1);

            BSDebug.TraceEnd(il, "Read " + type.TypeInfo.Name);

            il.Emit(OpCodes.Ret);
        }
        private static void GenerateObjectWriter(ILGenerator il, TypeImpl type)
        {
            var getRefId = typeof(RefWriterWatcher)
                           .GetTypeInfo()
                           .GetMethod(nameof(RefWriterWatcher.GetRefId), BindingFlags.Public | BindingFlags.Static);
            var onSerializing = typeof(IBinSerializable).GetMethod(nameof(IBinSerializable.OnSerializing));

            il.DeclareLocal(typeof(bool));
            il.DeclareLocal(typeof(SerializationInfo));

            var skipTypeLabel = il.DefineLabel();

            BSDebug.TraceStart(il, "Write " + type.TypeInfo.Name);

            // Invoke deserialzation callback
            if (typeof(IBinSerializable).IsAssignableFrom(type.Type))
            {
                il.Emit(OpCodes.Ldloca_S, (byte)1); // Load result local address
                il.Emit(OpCodes.Initobj, typeof(SerializationInfo));

                il.Emit(OpCodes.Ldarg_1);                 // Load serializing object
                il.Emit(OpCodes.Ldloc_1);                 // Load created SerializationInfo
                il.Emit(OpCodes.Callvirt, onSerializing); // Call onSerializing
            }

            // Write type
            il.Emit(OpCodes.Ldarg_0);
            il.Emit(OpCodes.Ldstr, SerializerTypes.GetTypeId(type));
            il.Emit(OpCodes.Call, SerializerTypes.TryGetStreamWriter(typeof(string)));

            if (!type.TypeInfo.IsValueType)
            {
                // Write refId
                il.Emit(OpCodes.Ldarg_0);           // Load stream
                il.Emit(OpCodes.Ldarg_1);           // Load obj
                il.Emit(OpCodes.Ldloca_S, (byte)0); // Load address of bool local
                il.Emit(OpCodes.Call, getRefId);    // Obj => RefId
                il.Emit(OpCodes.Call, SerializerTypes.TryGetStreamWriter(typeof(int)));

                // Check case when reference type already serialized.
                // If null be returned then created flag be zero too and serialization will be skipped.
                il.Emit(OpCodes.Ldloc_0);
                il.Emit(OpCodes.Brfalse, skipTypeLabel);
            }

            // Write type version
            var version = SerializerTypes.GetVersion(type);

            il.Emit(OpCodes.Ldarg_0);         // Load stream
            il.Emit(OpCodes.Ldc_I4, version); // Load version
            il.Emit(OpCodes.Call, SerializerTypes.TryGetStreamWriter(typeof(int)));

            // If writer exist then just write
            var writer = SerializerTypes.TryGetStreamWriter(type) ?? SerializerTypes.TryGetTypeWriter(type);

            if (writer != null)
            {
                il.Emit(OpCodes.Ldarg_0);      // Load stream
                il.Emit(OpCodes.Ldarg_1);      // Load obj
                il.Emit(OpCodes.Call, writer); // Write to stream
            }
            else
            {
                foreach (var binField in BinField.Get(type.Type))
                {
                    var skipFieldLabel = il.DefineLabel();

                    // If field is null then skip it
                    if (!binField.IsValueType)
                    {
                        il.Emit(OpCodes.Ldarg_1);                 // Load object
                        binField.EmitRead(il);                    // Read field form object
                        il.Emit(OpCodes.Brfalse, skipFieldLabel); // Skip if field is null
                    }

                    // Write field id
                    il.Emit(OpCodes.Ldarg_0);            // Load stream
                    il.Emit(OpCodes.Ldstr, binField.Id); // Load field id
                    il.Emit(OpCodes.Call, SerializerTypes.TryGetStreamWriter(typeof(string)));

                    var serialize = typeof(BinSerializer <>)
                                    .MakeGenericType(binField.Type)
                                    .GetTypeInfo()
                                    .GetMethod(nameof(BinSerializer <object> .Serialize), BindingFlags.Static | BindingFlags.Public);

                    // Write field
                    il.Emit(OpCodes.Ldarg_0);         // Load stream
                    il.Emit(OpCodes.Ldarg_1);         // Load object
                    binField.EmitRead(il);            // Read field from object
                    il.Emit(OpCodes.Call, serialize); // Write field value

                    // Skip field label
                    il.MarkLabel(skipFieldLabel);
                }

                // Write end id
                il.Emit(OpCodes.Ldarg_0);
                il.Emit(OpCodes.Ldstr, SerializerTypes.TypeEndToken);
                il.Emit(OpCodes.Call, SerializerTypes.TryGetStreamWriter(typeof(string)));
            }

            // Skip type label
            il.MarkLabel(skipTypeLabel);

            BSDebug.TraceEnd(il, "Write " + type.TypeInfo.Name);

            // End
            il.Emit(OpCodes.Ret);
        }
Exemple #4
0
 private static Dictionary <string, BinField> GetFieldsMap(Type type)
 {
     return(BinField.Get(type).ToDictionary(GetFieldId, GetField));
 }