From 0304c960cd16dbc8c556ad932a1b06d51a79ab61 Mon Sep 17 00:00:00 2001 From: Anairkoen Schno Date: Sun, 15 Dec 2019 20:48:48 -0600 Subject: [PATCH] Added support for converters --- IPA.Loader/Config/Stores/GeneratedStore.cs | 81 ++++++++++++++++++++-- 1 file changed, 75 insertions(+), 6 deletions(-) diff --git a/IPA.Loader/Config/Stores/GeneratedStore.cs b/IPA.Loader/Config/Stores/GeneratedStore.cs index 3ddf743b..75be9ab9 100644 --- a/IPA.Loader/Config/Stores/GeneratedStore.cs +++ b/IPA.Loader/Config/Stores/GeneratedStore.cs @@ -262,6 +262,7 @@ namespace IPA.Config.Stores public bool HasConverter; public bool IsGenericConverter; // used so we can call directly to the generic version if it is public Type Converter; + public Type ConverterBase; public Type ConverterTarget; public FieldInfo ConverterField; @@ -371,6 +372,10 @@ namespace IPA.Config.Stores } member.ConverterTarget = targetType; + if (member.IsGenericConverter) + member.ConverterBase = typeof(ValueConverter<>).MakeGenericType(targetType); + else + member.ConverterBase = typeof(IValueConverter); member.HasConverter = true; } @@ -1083,7 +1088,35 @@ namespace IPA.Config.Stores il.Emit(OpCodes.Call, member.Nullable_Value.GetGetMethod()); var targetType = GetExpectedValueTypeForType(member.Type); - if (targetType == typeof(Text)) + if (member.HasConverter) + { + var stlocal = GetLocal(member.Type); + if (member.IsGenericConverter) + { + var toValue = member.ConverterBase.GetMethod(nameof(ValueConverter.ToValue), + new[] { member.ConverterTarget, typeof(object) }); + + il.Emit(OpCodes.Stloc, stlocal); + il.Emit(OpCodes.Ldsfld, member.ConverterField); + il.Emit(OpCodes.Ldloc, stlocal); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Callvirt, toValue); + } + else + { + var toValue = typeof(IValueConverter).GetMethod(nameof(IValueConverter.ToValue), + new[] { typeof(object), typeof(object) }); + + il.Emit(OpCodes.Stloc, stlocal); + il.Emit(OpCodes.Ldsfld, member.ConverterField); + il.Emit(OpCodes.Ldloc, stlocal); + if (member.Type.IsValueType) + il.Emit(OpCodes.Box); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Callvirt, toValue); + } + } + 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)) @@ -1224,6 +1257,35 @@ namespace IPA.Config.Stores } } + private static void EmitDeserializeConverter(ILGenerator il, SerializedMemberInfo member, GetLocal GetLocal) + { + var stlocal = GetLocal(typeof(Value)); + if (member.IsGenericConverter) + { + var fromValue = member.ConverterBase.GetMethod(nameof(ValueConverter.FromValue), + new[] { typeof(Value), typeof(object) }); + + il.Emit(OpCodes.Stloc, stlocal); + il.Emit(OpCodes.Ldsfld, member.ConverterField); + il.Emit(OpCodes.Ldloc, stlocal); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Callvirt, fromValue); + } + else + { + var fromValue = typeof(IValueConverter).GetMethod(nameof(IValueConverter.FromValue), + new[] { typeof(Value), typeof(object) }); + + il.Emit(OpCodes.Stloc, stlocal); + il.Emit(OpCodes.Ldsfld, member.ConverterField); + il.Emit(OpCodes.Ldloc, stlocal); + if (member.Type.IsValueType) + il.Emit(OpCodes.Box); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Callvirt, fromValue); + } + } + // emit takes the value being deserialized, logs on error, leaves nothing on stack private static void EmitDeserializeMember(ILGenerator il, SerializedMemberInfo member, Label nextLabel, Action getValue, GetLocal GetLocal) { @@ -1261,14 +1323,20 @@ namespace IPA.Config.Stores 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 + if (member.HasConverter) + il.Emit(OpCodes.Br, passedTypeCheck); + else + { + 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 (expectType == typeof(FloatingPoint)) + if (member.HasConverter) { } // do nothing here + else if (expectType == typeof(FloatingPoint)) { var specialTypeCheck = il.DefineLabel(); il.Emit(OpCodes.Pop); @@ -1310,7 +1378,8 @@ namespace IPA.Config.Stores il.MarkLabel(passedTypeCheck); var local = GetLocal(member.Type, 0); - if (member.IsNullable) EmitDeserializeNullable(il, member, expectType, GetLocal); + if (member.HasConverter) EmitDeserializeConverter(il, member, GetLocal); + else if (member.IsNullable) EmitDeserializeNullable(il, member, expectType, GetLocal); else EmitDeserializeValue(il, member.Type, expectType, GetLocal); il.Emit(OpCodes.Stloc, local); EmitStore(il, member, il => il.Emit(OpCodes.Ldloc, local));