diff --git a/IPA.Loader/Config/Stores/Converters.cs b/IPA.Loader/Config/Stores/Converters.cs index 6c425288..e572a7df 100644 --- a/IPA.Loader/Config/Stores/Converters.cs +++ b/IPA.Loader/Config/Stores/Converters.cs @@ -36,16 +36,75 @@ namespace IPA.Config.Stores.Converters val is Integer inte ? inte.AsFloat()?.Value : null; + internal static Type GetDefaultConverterType(Type t, bool returnSimpleConverters = true) + { + if (t.IsEnum) + { + return typeof(CaseInsensitiveEnumConverter<>).MakeGenericType(t); + } + if (t.IsGenericType) + { + var generic = t.GetGenericTypeDefinition(); + var args = t.GetGenericArguments(); + if (generic == typeof(List<>)) + return (typeof(ListConverter<>).MakeGenericType(args)); + else if (generic == typeof(IList<>)) + return (typeof(IListConverter<>).MakeGenericType(args)); + else if (generic == typeof(Dictionary<,>) && args[0] == typeof(string)) + return (typeof(DictionaryConverter<>).MakeGenericType(args[1])); + else if (generic == typeof(IDictionary<,>) && args[0] == typeof(string)) + return (typeof(IDictionaryConverter<>).MakeGenericType(args[1])); +#if NET4 + else if (generic == typeof(ISet<>)) + return (typeof(ISetConverter<>).MakeGenericType(args)); + else if (generic == typeof(IReadOnlyDictionary<,>) && args[0] == typeof(string)) + return (typeof(IReadOnlyDictionaryConverter<>).MakeGenericType(args[1])); +#endif + } + var iCollBase = t.GetInterfaces() + .FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(ICollection<>)); + if (iCollBase != null && t.GetConstructor(Type.EmptyTypes) != null) + { // if it implements ICollection and has a default constructor + var valueType = iCollBase.GetGenericArguments().First(); + return (typeof(CollectionConverter<,>).MakeGenericType(valueType, t)); + } + if (!returnSimpleConverters) return null; + if (t == typeof(string)) + { + //Logger.log.Debug($"gives StringConverter"); + return typeof(StringConverter); + } + if (t.IsValueType) + { // we have to do this garbo to make it accept the thing that we know is a value type at instantiation time + if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>)) + { // this is a Nullable + //Logger.log.Debug($"gives NullableConverter<{Nullable.GetUnderlyingType(t)}>"); + return (typeof(NullableConverter<>).MakeGenericType(Nullable.GetUnderlyingType(t))); + } + + //Logger.log.Debug($"gives converter for value type {t}"); + var valConv = Activator.CreateInstance(typeof(ValConv<>).MakeGenericType(t)) as IValConv; + return valConv.Get(); + } + + //Logger.log.Debug($"gives CustomObjectConverter<{t}>"); + return (typeof(CustomObjectConverter<>).MakeGenericType(t)); + } + + internal interface IValConv + { + Type Get(); + } internal interface IValConv { - ValueConverter Get(); + Type Get(); } - internal class ValConv : IValConv where T : struct + internal class ValConv : IValConv, IValConv where T : struct { private static readonly IValConv Impl = ValConvImpls.Impl as IValConv ?? new ValConv(); - public ValueConverter Get() => Impl.Get(); - ValueConverter IValConv.Get() - => new CustomValueTypeConverter(); + public Type Get() => Impl.Get(); + Type IValConv.Get() + => typeof(CustomValueTypeConverter); } private class ValConvImpls : IValConv, IValConv, IValConv, @@ -57,21 +116,21 @@ namespace IPA.Config.Stores.Converters IValConv, IValConv { internal static readonly ValConvImpls Impl = new ValConvImpls(); - ValueConverter IValConv.Get() => new CharConverter(); - ValueConverter IValConv.Get() => new LongConverter(); - ValueConverter IValConv.Get() => new ULongConverter(); - ValueConverter IValConv.Get() => new IntPtrConverter(); - ValueConverter IValConv.Get() => new UIntPtrConverter(); - ValueConverter IValConv.Get() => new IntConverter(); - ValueConverter IValConv.Get() => new UIntConverter(); - ValueConverter IValConv.Get() => new ShortConverter(); - ValueConverter IValConv.Get() => new UShortConverter(); - ValueConverter IValConv.Get() => new ByteConverter(); - ValueConverter IValConv.Get() => new SByteConverter(); - ValueConverter IValConv.Get() => new FloatConverter(); - ValueConverter IValConv.Get() => new DoubleConverter(); - ValueConverter IValConv.Get() => new DecimalConverter(); - ValueConverter IValConv.Get() => new BooleanConverter(); + Type IValConv.Get() => typeof(CharConverter); + Type IValConv.Get() => typeof(LongConverter); + Type IValConv.Get() => typeof(ULongConverter); + Type IValConv.Get() => typeof(IntPtrConverter); + Type IValConv.Get() => typeof(UIntPtrConverter); + Type IValConv.Get() => typeof(IntConverter); + Type IValConv.Get() => typeof(UIntConverter); + Type IValConv.Get() => typeof(ShortConverter); + Type IValConv.Get() => typeof(UShortConverter); + Type IValConv.Get() => typeof(ByteConverter); + Type IValConv.Get() => typeof(SByteConverter); + Type IValConv.Get() => typeof(FloatConverter); + Type IValConv.Get() => typeof(DoubleConverter); + Type IValConv.Get() => typeof(DecimalConverter); + Type IValConv.Get() => typeof(BooleanConverter); } } @@ -88,7 +147,7 @@ namespace IPA.Config.Stores.Converters public static ValueConverter Default => defaultConverter ??= MakeDefault(); - internal static ValueConverter MakeDefault(bool allowValuesAndCustoms = true) + internal static ValueConverter MakeDefault(bool returnSimpleConverters = true) { var t = typeof(T); //Logger.log.Debug($"Converter<{t}>.MakeDefault()"); @@ -96,57 +155,7 @@ namespace IPA.Config.Stores.Converters static ValueConverter MakeInstOf(Type ty) => Activator.CreateInstance(ty) as ValueConverter; - if (t.IsEnum) - { - return MakeInstOf(typeof(CaseInsensitiveEnumConverter<>).MakeGenericType(t)); - } - if (t == typeof(string)) - { - //Logger.log.Debug($"gives StringConverter"); - return new StringConverter() as ValueConverter; - } - if (t.IsGenericType) - { - var generic = t.GetGenericTypeDefinition(); - var args = t.GetGenericArguments(); - if (generic == typeof(List<>)) - return MakeInstOf(typeof(ListConverter<>).MakeGenericType(args)); - else if (generic == typeof(IList<>)) - return MakeInstOf(typeof(IListConverter<>).MakeGenericType(args)); - else if (generic == typeof(Dictionary<,>) && args[0] == typeof(string)) - return MakeInstOf(typeof(DictionaryConverter<>).MakeGenericType(args[1])); - else if (generic == typeof(IDictionary<,>) && args[0] == typeof(string)) - return MakeInstOf(typeof(IDictionaryConverter<>).MakeGenericType(args[1])); -#if NET4 - else if (generic == typeof(ISet<>)) - return MakeInstOf(typeof(ISetConverter<>).MakeGenericType(args)); - else if (generic == typeof(IReadOnlyDictionary<,>) && args[0] == typeof(string)) - return MakeInstOf(typeof(IReadOnlyDictionaryConverter<>).MakeGenericType(args[1])); -#endif - } - var iCollBase = t.GetInterfaces() - .FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(ICollection<>)); - if (iCollBase != null && t.GetConstructor(Type.EmptyTypes) != null) - { // if it implements ICollection and has a default constructor - var valueType = iCollBase.GetGenericArguments().First(); - return MakeInstOf(typeof(CollectionConverter<,>).MakeGenericType(valueType, t)); - } - if (!allowValuesAndCustoms) return null; - if (t.IsValueType) - { // we have to do this garbo to make it accept the thing that we know is a value type at instantiation time - if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>)) - { // this is a Nullable - //Logger.log.Debug($"gives NullableConverter<{Nullable.GetUnderlyingType(t)}>"); - return MakeInstOf(typeof(NullableConverter<>).MakeGenericType(Nullable.GetUnderlyingType(t))); - } - - //Logger.log.Debug($"gives converter for value type {t}"); - var valConv = Activator.CreateInstance(typeof(Converter.ValConv<>).MakeGenericType(t)) as Converter.IValConv; - return valConv.Get(); - } - - //Logger.log.Debug($"gives CustomObjectConverter<{t}>"); - return MakeInstOf(typeof(CustomObjectConverter<>).MakeGenericType(t)); + return MakeInstOf(Converter.GetDefaultConverterType(t, returnSimpleConverters)); } } diff --git a/IPA.Loader/Config/Stores/GeneratedStoreImpl/ConversionDelegates.cs b/IPA.Loader/Config/Stores/GeneratedStoreImpl/ConversionDelegates.cs index cd46498f..0412b68e 100644 --- a/IPA.Loader/Config/Stores/GeneratedStoreImpl/ConversionDelegates.cs +++ b/IPA.Loader/Config/Stores/GeneratedStoreImpl/ConversionDelegates.cs @@ -41,6 +41,9 @@ namespace IPA.Config.Stores var loadObject = type.IsValueType ? (Action)(il => il.Emit(OpCodes.Ldarga_S, 0)) : il => il.Emit(OpCodes.Ldarg_0); + var loadParent = type.IsValueType + ? (Action)(il => il.Emit(OpCodes.Ldnull)) + : loadObject; { var il = dynMethod.GetILGenerator(); @@ -67,7 +70,7 @@ namespace IPA.Config.Stores } EmitLogError(il, $"Serializing structure of {type}"); - EmitSerializeStructure(il, structure, GetLocal, loadObject); + EmitSerializeStructure(il, structure, GetLocal, loadObject, loadParent); il.Emit(OpCodes.Ret); } diff --git a/IPA.Loader/Config/Stores/GeneratedStoreImpl/MakeCreator.cs b/IPA.Loader/Config/Stores/GeneratedStoreImpl/MakeCreator.cs index 18ad87b3..e35c20f2 100644 --- a/IPA.Loader/Config/Stores/GeneratedStoreImpl/MakeCreator.cs +++ b/IPA.Loader/Config/Stores/GeneratedStoreImpl/MakeCreator.cs @@ -361,7 +361,7 @@ namespace IPA.Config.Stores var GetLocal = MakeLocalAllocator(il); - EmitSerializeStructure(il, structure, GetLocal, GetMethodThis); + EmitSerializeStructure(il, structure, GetLocal, GetMethodThis, GetMethodThis); il.Emit(OpCodes.Ret); } diff --git a/IPA.Loader/Config/Stores/GeneratedStoreImpl/ObjectStructure.cs b/IPA.Loader/Config/Stores/GeneratedStoreImpl/ObjectStructure.cs index 9467ee7c..2ddefa3b 100644 --- a/IPA.Loader/Config/Stores/GeneratedStoreImpl/ObjectStructure.cs +++ b/IPA.Loader/Config/Stores/GeneratedStoreImpl/ObjectStructure.cs @@ -1,4 +1,5 @@ using IPA.Config.Stores.Attributes; +using IPA.Config.Stores.Converters; using IPA.Logging; using IPA.Utilities; using System; @@ -67,6 +68,11 @@ namespace IPA.Config.Stores member.HasConverter = false; var converterAttr = attrs.Select(o => o as UseConverterAttribute).NonNull().FirstOrDefault(); + if (converterAttr == null) + { + var defaultType = Converter.GetDefaultConverterType(member.Type, false); + if (defaultType != null) converterAttr = new UseConverterAttribute(defaultType); + } if (converterAttr != null) { member.Converter = converterAttr.ConverterType; diff --git a/IPA.Loader/Config/Stores/GeneratedStoreImpl/Serialization.cs b/IPA.Loader/Config/Stores/GeneratedStoreImpl/Serialization.cs index 262d39bf..cea03460 100644 --- a/IPA.Loader/Config/Stores/GeneratedStoreImpl/Serialization.cs +++ b/IPA.Loader/Config/Stores/GeneratedStoreImpl/Serialization.cs @@ -18,7 +18,7 @@ namespace IPA.Config.Stores internal static partial class GeneratedStoreImpl { // emit takes no args, leaves Value at top of stack - private static void EmitSerializeMember(ILGenerator il, SerializedMemberInfo member, LocalAllocator GetLocal, Action thisarg) + private static void EmitSerializeMember(ILGenerator il, SerializedMemberInfo member, LocalAllocator GetLocal, Action thisarg, Action parentobj) { EmitLoad(il, member, thisarg); @@ -137,7 +137,7 @@ namespace IPA.Config.Stores il.Emit(OpCodes.Isinst, typeof(IGeneratedStore)); il.Emit(OpCodes.Brtrue_S, noCreate); il.Emit(OpCodes.Stloc, stlocal); - EmitCreateChildGenerated(il, member.Type, GetMethodThis); + EmitCreateChildGenerated(il, member.Type, parentobj); il.Emit(OpCodes.Dup); il.Emit(OpCodes.Ldloc, stlocal); il.Emit(OpCodes.Ldc_I4_0); @@ -165,14 +165,14 @@ namespace IPA.Config.Stores il.Emit(OpCodes.Stloc, valueLocal); } - EmitSerializeStructure(il, structure, GetLocal, il => il.Emit(OpCodes.Ldloca, valueLocal)); + EmitSerializeStructure(il, structure, GetLocal, il => il.Emit(OpCodes.Ldloca, valueLocal), parentobj); } } il.MarkLabel(endSerialize); } - private static void EmitSerializeStructure(ILGenerator il, IEnumerable structure, LocalAllocator GetLocal, Action thisarg) + private static void EmitSerializeStructure(ILGenerator il, IEnumerable structure, LocalAllocator GetLocal, Action thisarg, Action parentobj) { var MapCreate = typeof(Value).GetMethod(nameof(Value.Map)); var MapAdd = typeof(Map).GetMethod(nameof(Map.Add)); @@ -185,7 +185,7 @@ namespace IPA.Config.Stores foreach (var mem in structure) { - EmitSerializeMember(il, mem, GetLocal, thisarg); + EmitSerializeMember(il, mem, GetLocal, thisarg, parentobj); il.Emit(OpCodes.Stloc, valueLocal); il.Emit(OpCodes.Ldloc, mapLocal); il.Emit(OpCodes.Ldstr, mem.Name);