예제 #1
0
        private static void EmitDeserializeConverter(ILGenerator il, SerializedMemberInfo member, Label nextLabel, LocalAllocator GetLocal,
                                                     Action <ILGenerator> thisobj, Action <ILGenerator> parentobj)
        {
            if (!member.HasConverter)
            {
                throw new InvalidOperationException("EmitDeserializeConverter called for member without converter");
            }

            using var stlocal  = GetLocal.Allocate(typeof(Value));
            using var valLocal = GetLocal.Allocate(member.Type);

            il.Emit(OpCodes.Stloc, stlocal);
            il.BeginExceptionBlock();
            il.Emit(OpCodes.Ldsfld, member.ConverterField);
            il.Emit(OpCodes.Ldloc, stlocal);
            parentobj(il);

            if (member.IsGenericConverter)
            {
                var fromValueBase = member.ConverterBase.GetMethod(nameof(ValueConverter <int> .FromValue),
                                                                   new[] { typeof(Value), typeof(object) });
                var fromValue = member.Converter.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
                                .FirstOrDefault(m => m.GetBaseDefinition() == fromValueBase) ?? fromValueBase;
                il.Emit(OpCodes.Call, fromValue);
            }
            else
            {
                var fromValueBase = typeof(IValueConverter).GetMethod(nameof(IValueConverter.FromValue),
                                                                      new[] { typeof(Value), typeof(object) });
                var fromValue = member.Converter.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
                                .FirstOrDefault(m => m.GetBaseDefinition() == fromValueBase) ?? fromValueBase;
                il.Emit(OpCodes.Call, fromValue);
                if (member.Type.IsValueType)
                {
                    il.Emit(OpCodes.Unbox);
                }
            }

            il.Emit(OpCodes.Stloc, valLocal);
            il.BeginCatchBlock(typeof(Exception));
            EmitWarnException(il, "Error occurred while deserializing");
            il.Emit(OpCodes.Leave, nextLabel);
            il.EndExceptionBlock();
            il.Emit(OpCodes.Ldloc, valLocal);
        }
예제 #2
0
        private static void EmitSerializeStructure(ILGenerator il, IEnumerable <SerializedMemberInfo> structure, LocalAllocator GetLocal, Action <ILGenerator> thisarg, Action <ILGenerator> parentobj)
        {
            var MapCreate = typeof(Value).GetMethod(nameof(Value.Map));
            var MapAdd    = typeof(Map).GetMethod(nameof(Map.Add));

            using var mapLocal   = GetLocal.Allocate(typeof(Map));
            using var valueLocal = GetLocal.Allocate(typeof(Value));

            il.Emit(OpCodes.Call, MapCreate);
            il.Emit(OpCodes.Stloc, mapLocal);

            foreach (var mem in structure)
            {
                EmitSerializeMember(il, mem, GetLocal, thisarg, parentobj);
                il.Emit(OpCodes.Stloc, valueLocal);
                il.Emit(OpCodes.Ldloc, mapLocal);
                il.Emit(OpCodes.Ldstr, mem.Name);
                il.Emit(OpCodes.Ldloc, valueLocal);
                il.Emit(OpCodes.Call, MapAdd);
            }

            il.Emit(OpCodes.Ldloc, mapLocal);
        }
예제 #3
0
        private static void EmitDeserializeGeneratedValue(ILGenerator il, SerializedMemberInfo member, Type srcType, LocalAllocator GetLocal,
                                                          Action <ILGenerator> thisarg, Action <ILGenerator> parentobj)
        {
            var IGeneratedStore_Deserialize = typeof(IGeneratedStore).GetMethod(nameof(IGeneratedStore.Deserialize));

            using var valuel = GetLocal.Allocate(srcType);
            var noCreate = il.DefineLabel();

            il.Emit(OpCodes.Stloc, valuel);
            EmitLoad(il, member, thisarg);
            il.Emit(OpCodes.Dup);
            il.Emit(OpCodes.Isinst, typeof(IGeneratedStore));
            il.Emit(OpCodes.Brtrue_S, noCreate);
            il.Emit(OpCodes.Pop);
            EmitCreateChildGenerated(il, member.Type, parentobj);
            il.MarkLabel(noCreate);

            il.Emit(OpCodes.Dup);
            il.Emit(OpCodes.Ldloc, valuel);
            il.Emit(OpCodes.Callvirt, IGeneratedStore_Deserialize);
        }
예제 #4
0
        // emit takes no args, leaves Value at top of stack
        private static void EmitSerializeMember(ILGenerator il, SerializedMemberInfo member, LocalAllocator GetLocal, Action <ILGenerator> thisarg, Action <ILGenerator> parentobj)
        {
            EmitLoad(il, member, thisarg);

            var endSerialize = il.DefineLabel();

            if (member.AllowNull)
            {
                var passedNull = il.DefineLabel();

                il.Emit(OpCodes.Dup);
                if (member.IsNullable)
                {
                    il.Emit(OpCodes.Call, member.Nullable_HasValue.GetGetMethod());
                }
                il.Emit(OpCodes.Brtrue, passedNull);

                il.Emit(OpCodes.Pop);
                il.Emit(OpCodes.Ldnull);
                il.Emit(OpCodes.Br, endSerialize);

                il.MarkLabel(passedNull);
            }

            if (member.IsNullable)
            {
                il.Emit(OpCodes.Call, member.Nullable_Value.GetGetMethod());
            }

            var memberConversionType = member.ConversionType;
            var targetType           = GetExpectedValueTypeForType(memberConversionType);

            if (member.HasConverter)
            {
                using var stlocal  = GetLocal.Allocate(member.Type);
                using var valLocal = GetLocal.Allocate(typeof(Value));

                il.Emit(OpCodes.Stloc, stlocal);
                il.BeginExceptionBlock();
                il.Emit(OpCodes.Ldsfld, member.ConverterField);
                il.Emit(OpCodes.Ldloc, stlocal);

                if (member.IsGenericConverter)
                {
                    var toValueBase = member.ConverterBase.GetMethod(nameof(ValueConverter <int> .ToValue),
                                                                     new[] { member.ConverterTarget, typeof(object) });
                    var toValue = member.Converter.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
                                  .FirstOrDefault(m => m.GetBaseDefinition() == toValueBase) ?? toValueBase;
                    il.Emit(OpCodes.Ldarg_0);
                    il.Emit(OpCodes.Call, toValue);
                }
                else
                {
                    var toValueBase = typeof(IValueConverter).GetMethod(nameof(IValueConverter.ToValue),
                                                                        new[] { typeof(object), typeof(object) });
                    var toValue = member.Converter.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
                                  .FirstOrDefault(m => m.GetBaseDefinition() == toValueBase) ?? toValueBase;
                    il.Emit(OpCodes.Box);
                    il.Emit(OpCodes.Ldarg_0);
                    il.Emit(OpCodes.Call, toValue);
                }

                il.Emit(OpCodes.Stloc, valLocal);
                il.BeginCatchBlock(typeof(Exception));
                EmitWarnException(il, "Error serializing member using converter");
                il.Emit(OpCodes.Ldnull);
                il.Emit(OpCodes.Stloc, valLocal);
                il.EndExceptionBlock();
                il.Emit(OpCodes.Ldloc, valLocal);
            }
            else if (targetType == typeof(Text))
            { // only happens when arg is a string or char
                var TextCreate = typeof(Value).GetMethod(nameof(Value.Text));
                if (member.Type == typeof(char))
                {
                    var strFromChar = typeof(char).GetMethod(nameof(char.ToString), new[] { typeof(char) });
                    il.Emit(OpCodes.Call, strFromChar);
                }
                il.Emit(OpCodes.Call, TextCreate);
            }
            else if (targetType == typeof(Boolean))
            {
                var BoolCreate = typeof(Value).GetMethod(nameof(Value.Bool));
                il.Emit(OpCodes.Call, BoolCreate);
            }
            else if (targetType == typeof(Integer))
            {
                var IntCreate = typeof(Value).GetMethod(nameof(Value.Integer));
                EmitNumberConvertTo(il, IntCreate.GetParameters()[0].ParameterType, member.Type);
                il.Emit(OpCodes.Call, IntCreate);
            }
            else if (targetType == typeof(FloatingPoint))
            {
                var FloatCreate = typeof(Value).GetMethod(nameof(Value.Float));
                EmitNumberConvertTo(il, FloatCreate.GetParameters()[0].ParameterType, member.Type);
                il.Emit(OpCodes.Call, FloatCreate);
            }
            else if (targetType == typeof(List))
            {
                // TODO: impl this (enumerables)
                Logger.config.Warn($"Implicit conversions to {targetType} are not currently implemented");
                il.Emit(OpCodes.Pop);
                il.Emit(OpCodes.Ldnull);
            }
            else if (targetType == typeof(Map))
            {
                // TODO: support other aggregate types
                if (!memberConversionType.IsValueType)
                {
                    // if it is a reference type, we assume that its a generated type implementing IGeneratedStore
                    var IGeneratedStore_Serialize = typeof(IGeneratedStore).GetMethod(nameof(IGeneratedStore.Serialize));
                    var IGeneratedStoreT_CopyFrom = typeof(IGeneratedStore <>).MakeGenericType(member.Type)
                                                    .GetMethod(nameof(IGeneratedStore <object> .CopyFrom));

                    if (!member.IsVirtual)
                    {
                        var noCreate = il.DefineLabel();
                        using var stlocal = GetLocal.Allocate(member.Type);

                        // first check to make sure that this is an IGeneratedStore, because we don't control assignments to it
                        il.Emit(OpCodes.Dup);
                        il.Emit(OpCodes.Isinst, typeof(IGeneratedStore));
                        il.Emit(OpCodes.Brtrue_S, noCreate);
                        il.Emit(OpCodes.Stloc, stlocal);
                        EmitCreateChildGenerated(il, member.Type, parentobj);
                        il.Emit(OpCodes.Dup);
                        il.Emit(OpCodes.Ldloc, stlocal);
                        il.Emit(OpCodes.Ldc_I4_0);
                        il.Emit(OpCodes.Callvirt, IGeneratedStoreT_CopyFrom);
                        il.Emit(OpCodes.Dup);
                        il.Emit(OpCodes.Stloc, stlocal);
                        EmitStore(il, member, il => il.Emit(OpCodes.Ldloc, stlocal), thisarg);
                        il.MarkLabel(noCreate);
                    }
                    il.Emit(OpCodes.Callvirt, IGeneratedStore_Serialize);
                }
                else
                { // generate serialization for value types
                    using var valueLocal = GetLocal.Allocate(memberConversionType);

                    var structure = ReadObjectMembers(memberConversionType);
                    if (!structure.Any())
                    {
                        Logger.config.Warn($"Custom value type {memberConversionType.FullName} (when compiling serialization of" +
                                           $" {member.Name} on {member.Member.DeclaringType.FullName}) has no accessible members");
                        il.Emit(OpCodes.Pop);
                    }
                    else
                    {
                        il.Emit(OpCodes.Stloc, valueLocal);
                    }

                    EmitSerializeStructure(il, structure, GetLocal, il => il.Emit(OpCodes.Ldloca, valueLocal), parentobj);
                }
            }

            il.MarkLabel(endSerialize);
        }
예제 #5
0
        private static void EmitDeserializeValue(ILGenerator il, SerializedMemberInfo member, Type targetType, Type expected, LocalAllocator GetLocal,
                                                 Action <ILGenerator> thisarg, Action <ILGenerator> parentobj)
        {
            if (typeof(Value).IsAssignableFrom(targetType))
            {
                return;                                             // do nothing
            }
            if (expected == typeof(Text))
            {
                var getter = expected.GetProperty(nameof(Text.Value)).GetGetMethod();
                il.Emit(OpCodes.Call, getter);
                if (targetType == typeof(char))
                {
                    var strIndex = typeof(string).GetProperty("Chars").GetGetMethod(); // string's indexer is specially named Chars
                    il.Emit(OpCodes.Ldc_I4_0);
                    il.Emit(OpCodes.Call, strIndex);
                }
            }
            else if (expected == typeof(Boolean))
            {
                var getter = expected.GetProperty(nameof(Boolean.Value)).GetGetMethod();
                il.Emit(OpCodes.Call, getter);
            }
            else if (expected == typeof(Integer))
            {
                var getter = expected.GetProperty(nameof(Integer.Value)).GetGetMethod();
                il.Emit(OpCodes.Call, getter);
                EmitNumberConvertTo(il, targetType, getter.ReturnType);
            }
            else if (expected == typeof(FloatingPoint))
            {
                var getter = expected.GetProperty(nameof(FloatingPoint.Value)).GetGetMethod();
                il.Emit(OpCodes.Call, getter);
                EmitNumberConvertTo(il, targetType, getter.ReturnType);
            } // TODO: implement stuff for lists and maps of various types (probably call out somewhere else to figure out what to do)
            else if (expected == typeof(Map))
            {
                if (!targetType.IsValueType)
                {
                    EmitDeserializeGeneratedValue(il, member, expected, GetLocal, thisarg, parentobj);
                }
                else
                {
                    using var mapLocal    = GetLocal.Allocate(typeof(Map));
                    using var resultLocal = GetLocal.Allocate(targetType);
                    using var valueLocal  = GetLocal.Allocate(typeof(Value));

                    var structure = ReadObjectMembers(targetType);
                    if (!structure.Any())
                    {
                        Logger.Config.Warn($"Custom value type {targetType.FullName} (when compiling serialization of" +
                                           $" {member.Name} on {member.Member.DeclaringType.FullName}) has no accessible members");
                        il.Emit(OpCodes.Pop);
                        il.Emit(OpCodes.Ldloca, resultLocal);
                        il.Emit(OpCodes.Initobj, targetType);
                    }
                    else
                    {
                        il.Emit(OpCodes.Stloc, mapLocal);

                        EmitLoad(il, member, thisarg);
                        il.Emit(OpCodes.Stloc, resultLocal);

                        EmitDeserializeStructure(il, structure, mapLocal, valueLocal, GetLocal, il => il.Emit(OpCodes.Ldloca, resultLocal), parentobj);
                    }

                    il.Emit(OpCodes.Ldloc, resultLocal);
                }
            }
            else
            {
                Logger.Config.Warn($"Implicit conversions to {expected} are not currently implemented");
                il.Emit(OpCodes.Pop);
                il.Emit(OpCodes.Ldnull);
            }
        }
예제 #6
0
        private static void EmitDeserializeMember(ILGenerator il, SerializedMemberInfo member, Label nextLabel, Action <ILGenerator> getValue, LocalAllocator GetLocal,
                                                  Action <ILGenerator> thisobj, Action <ILGenerator> parentobj)
        {
            var Object_GetType = typeof(object).GetMethod(nameof(Object.GetType));

            var implLabel       = il.DefineLabel();
            var passedTypeCheck = il.DefineLabel();
            var expectType      = GetExpectedValueTypeForType(member.ConversionType);

            il.Emit(OpCodes.Dup);
            il.Emit(OpCodes.Brtrue_S, implLabel); // null check

            if (!member.AllowNull)
            {
                il.Emit(OpCodes.Pop);
                EmitLogError(il, $"Member {member.Name} ({member.Type}) not nullable", tailcall: false,
                             expected: il => EmitTypeof(il, expectType));
                il.Emit(OpCodes.Br, nextLabel);
            }
            else if (member.IsNullable)
            {
                il.Emit(OpCodes.Pop);
                using var valTLocal = GetLocal.Allocate(member.Type);
                il.Emit(OpCodes.Ldloca, valTLocal);
                il.Emit(OpCodes.Initobj, member.Type);
                EmitStore(il, member, il => il.Emit(OpCodes.Ldloc, valTLocal), thisobj);
                il.Emit(OpCodes.Br, nextLabel);
            }
            else
            {
                il.Emit(OpCodes.Pop);
                EmitStore(il, member, il => il.Emit(OpCodes.Ldnull), thisobj);
                il.Emit(OpCodes.Br, nextLabel);
            }


            if (!member.HasConverter)
            {
                il.MarkLabel(implLabel);
                il.Emit(OpCodes.Isinst, expectType);      //replaces on stack
                il.Emit(OpCodes.Dup);                     // duplicate cloned value
                il.Emit(OpCodes.Brtrue, passedTypeCheck); // null check
            }

            var errorHandle = il.DefineLabel();

            // special cases to handle coersion between Float and Int
            if (member.HasConverter)
            {
                il.MarkLabel(implLabel);
            }
            else if (expectType == typeof(FloatingPoint))
            {
                var specialTypeCheck = il.DefineLabel();
                il.Emit(OpCodes.Pop);
                getValue(il);
                il.Emit(OpCodes.Isinst, typeof(Integer)); //replaces on stack
                il.Emit(OpCodes.Dup);                     // duplicate cloned value
                il.Emit(OpCodes.Brfalse, errorHandle);    // null check

                var Integer_CoerceToFloat = typeof(Integer).GetMethod(nameof(Integer.AsFloat));
                il.Emit(OpCodes.Call, Integer_CoerceToFloat);

                il.Emit(OpCodes.Br, passedTypeCheck);
            }
            else if (expectType == typeof(Integer))
            {
                var specialTypeCheck = il.DefineLabel();
                il.Emit(OpCodes.Pop);
                getValue(il);
                il.Emit(OpCodes.Isinst, typeof(FloatingPoint)); //replaces on stack
                il.Emit(OpCodes.Dup);                           // duplicate cloned value
                il.Emit(OpCodes.Brfalse, errorHandle);          // null check

                var Float_CoerceToInt = typeof(FloatingPoint).GetMethod(nameof(FloatingPoint.AsInteger));
                il.Emit(OpCodes.Call, Float_CoerceToInt);

                il.Emit(OpCodes.Br, passedTypeCheck);
            }

            if (!member.HasConverter)
            {
                il.MarkLabel(errorHandle);
                il.Emit(OpCodes.Pop);
                EmitLogError(il, $"Unexpected type deserializing {member.Name}", tailcall: false,
                             expected: il => EmitTypeof(il, expectType), found: il =>
                {
                    getValue(il);
                    il.Emit(OpCodes.Callvirt, Object_GetType);
                });
                il.Emit(OpCodes.Br, nextLabel);
            }

            il.MarkLabel(passedTypeCheck);

            using var local = GetLocal.Allocate(member.Type);
            if (member.HasConverter)
            {
                EmitDeserializeConverter(il, member, nextLabel, GetLocal, thisobj, parentobj);
            }
            else if (member.IsNullable)
            {
                EmitDeserializeNullable(il, member, expectType, GetLocal, thisobj, parentobj);
            }
            else
            {
                EmitDeserializeValue(il, member, member.Type, expectType, GetLocal, thisobj, parentobj);
            }
            il.Emit(OpCodes.Stloc, local);
            EmitStore(il, member, il => il.Emit(OpCodes.Ldloc, local), thisobj);
        }
예제 #7
0
        private static void EmitCorrectMember(ILGenerator il, SerializedMemberInfo member, bool shouldLock, bool alwaysNew, LocalAllocator GetLocal,
                                              Action <ILGenerator> thisobj, Action <ILGenerator> parentobj)
        {
            if (!NeedsCorrection(member))
            {
                return;
            }

            var endLabel = il.DefineLabel();

            if (member.IsNullable)
            {
                il.Emit(OpCodes.Dup);
                il.Emit(OpCodes.Call, member.Nullable_HasValue.GetGetMethod());
                il.Emit(OpCodes.Brfalse, endLabel);
                il.Emit(OpCodes.Call, member.Nullable_Value.GetGetMethod());
            }

            var convType = member.ConversionType;

            if (!convType.IsValueType)
            {
                // currently the only thing for this is where expect == Map, so do generate shit
                var copyFrom = typeof(IGeneratedStore <>).MakeGenericType(convType).GetMethod(nameof(IGeneratedStore <Config> .CopyFrom));
                var noCreate = il.DefineLabel();
                using var valLocal = GetLocal.Allocate(convType);

                if (member.AllowNull)
                {
                    il.Emit(OpCodes.Dup);
                    il.Emit(OpCodes.Brfalse_S, endLabel); // thing is null, just bypass it all
                }
                if (!alwaysNew)
                {
                    il.Emit(OpCodes.Dup);
                    il.Emit(OpCodes.Isinst, typeof(IGeneratedStore));
                    il.Emit(OpCodes.Brtrue_S, endLabel); // our input is already something we like
                }
                il.Emit(OpCodes.Stloc, valLocal);
                if (!alwaysNew)
                {
                    EmitLoad(il, member, thisobj);
                    il.Emit(OpCodes.Dup);
                    il.Emit(OpCodes.Isinst, typeof(IGeneratedStore));
                    il.Emit(OpCodes.Brtrue_S, noCreate);
                    il.Emit(OpCodes.Pop);
                }
                EmitCreateChildGenerated(il, convType, parentobj);
                il.MarkLabel(noCreate);

                il.Emit(OpCodes.Dup);
                il.Emit(OpCodes.Ldloc, valLocal);
                il.Emit(shouldLock ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0);
                il.Emit(OpCodes.Callvirt, copyFrom);
            }
            else
            {
                // for special value types, we'll go ahead and correct each of their members
                var structure = ReadObjectMembers(convType);

                using var valueLocal = GetLocal.Allocate(convType);
                il.Emit(OpCodes.Stloc, valueLocal);

                void LdlocaValueLocal(ILGenerator il)
                => il.Emit(OpCodes.Ldloca, valueLocal);

                foreach (var mem in structure)
                {
                    if (NeedsCorrection(mem))
                    {
                        EmitLoadCorrectStore(il, mem, shouldLock, alwaysNew, GetLocal,
                                             LdlocaValueLocal, LdlocaValueLocal, parentobj);
                    }
                }

                il.Emit(OpCodes.Ldloc, valueLocal);
            }

            if (member.IsNullable)
            {
                il.Emit(OpCodes.Newobj, member.Nullable_Construct);
            }

            il.MarkLabel(endLabel);
        }