Browse Source

Added automatic converter application for types that have a default converter

pull/44/head
Anairkoen Schno 4 years ago
parent
commit
be9db24185
5 changed files with 97 additions and 79 deletions
  1. +81
    -72
      IPA.Loader/Config/Stores/Converters.cs
  2. +4
    -1
      IPA.Loader/Config/Stores/GeneratedStoreImpl/ConversionDelegates.cs
  3. +1
    -1
      IPA.Loader/Config/Stores/GeneratedStoreImpl/MakeCreator.cs
  4. +6
    -0
      IPA.Loader/Config/Stores/GeneratedStoreImpl/ObjectStructure.cs
  5. +5
    -5
      IPA.Loader/Config/Stores/GeneratedStoreImpl/Serialization.cs

+ 81
- 72
IPA.Loader/Config/Stores/Converters.cs View File

@ -36,16 +36,75 @@ namespace IPA.Config.Stores.Converters
val is Integer inte ? inte.AsFloat()?.Value : val is Integer inte ? inte.AsFloat()?.Value :
null; 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<T> internal interface IValConv<T>
{ {
ValueConverter<T> Get();
Type Get();
} }
internal class ValConv<T> : IValConv<T> where T : struct
internal class ValConv<T> : IValConv, IValConv<T> where T : struct
{ {
private static readonly IValConv<T> Impl = ValConvImpls.Impl as IValConv<T> ?? new ValConv<T>(); private static readonly IValConv<T> Impl = ValConvImpls.Impl as IValConv<T> ?? new ValConv<T>();
public ValueConverter<T> Get() => Impl.Get();
ValueConverter<T> IValConv<T>.Get()
=> new CustomValueTypeConverter<T>();
public Type Get() => Impl.Get();
Type IValConv<T>.Get()
=> typeof(CustomValueTypeConverter<T>);
} }
private class ValConvImpls : IValConv<char>, private class ValConvImpls : IValConv<char>,
IValConv<IntPtr>, IValConv<UIntPtr>, IValConv<IntPtr>, IValConv<UIntPtr>,
@ -57,21 +116,21 @@ namespace IPA.Config.Stores.Converters
IValConv<decimal>, IValConv<bool> IValConv<decimal>, IValConv<bool>
{ {
internal static readonly ValConvImpls Impl = new ValConvImpls(); internal static readonly ValConvImpls Impl = new ValConvImpls();
ValueConverter<char> IValConv<char>.Get() => new CharConverter();
ValueConverter<long> IValConv<long>.Get() => new LongConverter();
ValueConverter<ulong> IValConv<ulong>.Get() => new ULongConverter();
ValueConverter<IntPtr> IValConv<IntPtr>.Get() => new IntPtrConverter();
ValueConverter<UIntPtr> IValConv<UIntPtr>.Get() => new UIntPtrConverter();
ValueConverter<int> IValConv<int>.Get() => new IntConverter();
ValueConverter<uint> IValConv<uint>.Get() => new UIntConverter();
ValueConverter<short> IValConv<short>.Get() => new ShortConverter();
ValueConverter<ushort> IValConv<ushort>.Get() => new UShortConverter();
ValueConverter<byte> IValConv<byte>.Get() => new ByteConverter();
ValueConverter<sbyte> IValConv<sbyte>.Get() => new SByteConverter();
ValueConverter<float> IValConv<float>.Get() => new FloatConverter();
ValueConverter<double> IValConv<double>.Get() => new DoubleConverter();
ValueConverter<decimal> IValConv<decimal>.Get() => new DecimalConverter();
ValueConverter<bool> IValConv<bool>.Get() => new BooleanConverter();
Type IValConv<char>.Get() => typeof(CharConverter);
Type IValConv<long>.Get() => typeof(LongConverter);
Type IValConv<ulong>.Get() => typeof(ULongConverter);
Type IValConv<IntPtr>.Get() => typeof(IntPtrConverter);
Type IValConv<UIntPtr>.Get() => typeof(UIntPtrConverter);
Type IValConv<int>.Get() => typeof(IntConverter);
Type IValConv<uint>.Get() => typeof(UIntConverter);
Type IValConv<short>.Get() => typeof(ShortConverter);
Type IValConv<ushort>.Get() => typeof(UShortConverter);
Type IValConv<byte>.Get() => typeof(ByteConverter);
Type IValConv<sbyte>.Get() => typeof(SByteConverter);
Type IValConv<float>.Get() => typeof(FloatConverter);
Type IValConv<double>.Get() => typeof(DoubleConverter);
Type IValConv<decimal>.Get() => typeof(DecimalConverter);
Type IValConv<bool>.Get() => typeof(BooleanConverter);
} }
} }
@ -88,7 +147,7 @@ namespace IPA.Config.Stores.Converters
public static ValueConverter<T> Default public static ValueConverter<T> Default
=> defaultConverter ??= MakeDefault(); => defaultConverter ??= MakeDefault();
internal static ValueConverter<T> MakeDefault(bool allowValuesAndCustoms = true)
internal static ValueConverter<T> MakeDefault(bool returnSimpleConverters = true)
{ {
var t = typeof(T); var t = typeof(T);
//Logger.log.Debug($"Converter<{t}>.MakeDefault()"); //Logger.log.Debug($"Converter<{t}>.MakeDefault()");
@ -96,57 +155,7 @@ namespace IPA.Config.Stores.Converters
static ValueConverter<T> MakeInstOf(Type ty) static ValueConverter<T> MakeInstOf(Type ty)
=> Activator.CreateInstance(ty) as ValueConverter<T>; => Activator.CreateInstance(ty) as ValueConverter<T>;
if (t.IsEnum)
{
return MakeInstOf(typeof(CaseInsensitiveEnumConverter<>).MakeGenericType(t));
}
if (t == typeof(string))
{
//Logger.log.Debug($"gives StringConverter");
return new StringConverter() as ValueConverter<T>;
}
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<T>;
return valConv.Get();
}
//Logger.log.Debug($"gives CustomObjectConverter<{t}>");
return MakeInstOf(typeof(CustomObjectConverter<>).MakeGenericType(t));
return MakeInstOf(Converter.GetDefaultConverterType(t, returnSimpleConverters));
} }
} }


+ 4
- 1
IPA.Loader/Config/Stores/GeneratedStoreImpl/ConversionDelegates.cs View File

@ -41,6 +41,9 @@ namespace IPA.Config.Stores
var loadObject = type.IsValueType var loadObject = type.IsValueType
? (Action<ILGenerator>)(il => il.Emit(OpCodes.Ldarga_S, 0)) ? (Action<ILGenerator>)(il => il.Emit(OpCodes.Ldarga_S, 0))
: il => il.Emit(OpCodes.Ldarg_0); : il => il.Emit(OpCodes.Ldarg_0);
var loadParent = type.IsValueType
? (Action<ILGenerator>)(il => il.Emit(OpCodes.Ldnull))
: loadObject;
{ {
var il = dynMethod.GetILGenerator(); var il = dynMethod.GetILGenerator();
@ -67,7 +70,7 @@ namespace IPA.Config.Stores
} }
EmitLogError(il, $"Serializing structure of {type}"); EmitLogError(il, $"Serializing structure of {type}");
EmitSerializeStructure(il, structure, GetLocal, loadObject);
EmitSerializeStructure(il, structure, GetLocal, loadObject, loadParent);
il.Emit(OpCodes.Ret); il.Emit(OpCodes.Ret);
} }


+ 1
- 1
IPA.Loader/Config/Stores/GeneratedStoreImpl/MakeCreator.cs View File

@ -361,7 +361,7 @@ namespace IPA.Config.Stores
var GetLocal = MakeLocalAllocator(il); var GetLocal = MakeLocalAllocator(il);
EmitSerializeStructure(il, structure, GetLocal, GetMethodThis);
EmitSerializeStructure(il, structure, GetLocal, GetMethodThis, GetMethodThis);
il.Emit(OpCodes.Ret); il.Emit(OpCodes.Ret);
} }


+ 6
- 0
IPA.Loader/Config/Stores/GeneratedStoreImpl/ObjectStructure.cs View File

@ -1,4 +1,5 @@
using IPA.Config.Stores.Attributes; using IPA.Config.Stores.Attributes;
using IPA.Config.Stores.Converters;
using IPA.Logging; using IPA.Logging;
using IPA.Utilities; using IPA.Utilities;
using System; using System;
@ -67,6 +68,11 @@ namespace IPA.Config.Stores
member.HasConverter = false; member.HasConverter = false;
var converterAttr = attrs.Select(o => o as UseConverterAttribute).NonNull().FirstOrDefault(); 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) if (converterAttr != null)
{ {
member.Converter = converterAttr.ConverterType; member.Converter = converterAttr.ConverterType;


+ 5
- 5
IPA.Loader/Config/Stores/GeneratedStoreImpl/Serialization.cs View File

@ -18,7 +18,7 @@ namespace IPA.Config.Stores
internal static partial class GeneratedStoreImpl internal static partial class GeneratedStoreImpl
{ {
// emit takes no args, leaves Value at top of stack // emit takes no args, leaves Value at top of stack
private static void EmitSerializeMember(ILGenerator il, SerializedMemberInfo member, LocalAllocator GetLocal, Action<ILGenerator> thisarg)
private static void EmitSerializeMember(ILGenerator il, SerializedMemberInfo member, LocalAllocator GetLocal, Action<ILGenerator> thisarg, Action<ILGenerator> parentobj)
{ {
EmitLoad(il, member, thisarg); EmitLoad(il, member, thisarg);
@ -137,7 +137,7 @@ namespace IPA.Config.Stores
il.Emit(OpCodes.Isinst, typeof(IGeneratedStore)); il.Emit(OpCodes.Isinst, typeof(IGeneratedStore));
il.Emit(OpCodes.Brtrue_S, noCreate); il.Emit(OpCodes.Brtrue_S, noCreate);
il.Emit(OpCodes.Stloc, stlocal); il.Emit(OpCodes.Stloc, stlocal);
EmitCreateChildGenerated(il, member.Type, GetMethodThis);
EmitCreateChildGenerated(il, member.Type, parentobj);
il.Emit(OpCodes.Dup); il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Ldloc, stlocal); il.Emit(OpCodes.Ldloc, stlocal);
il.Emit(OpCodes.Ldc_I4_0); il.Emit(OpCodes.Ldc_I4_0);
@ -165,14 +165,14 @@ namespace IPA.Config.Stores
il.Emit(OpCodes.Stloc, valueLocal); 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); il.MarkLabel(endSerialize);
} }
private static void EmitSerializeStructure(ILGenerator il, IEnumerable<SerializedMemberInfo> structure, LocalAllocator GetLocal, Action<ILGenerator> thisarg)
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 MapCreate = typeof(Value).GetMethod(nameof(Value.Map));
var MapAdd = typeof(Map).GetMethod(nameof(Map.Add)); var MapAdd = typeof(Map).GetMethod(nameof(Map.Add));
@ -185,7 +185,7 @@ namespace IPA.Config.Stores
foreach (var mem in structure) foreach (var mem in structure)
{ {
EmitSerializeMember(il, mem, GetLocal, thisarg);
EmitSerializeMember(il, mem, GetLocal, thisarg, parentobj);
il.Emit(OpCodes.Stloc, valueLocal); il.Emit(OpCodes.Stloc, valueLocal);
il.Emit(OpCodes.Ldloc, mapLocal); il.Emit(OpCodes.Ldloc, mapLocal);
il.Emit(OpCodes.Ldstr, mem.Name); il.Emit(OpCodes.Ldstr, mem.Name);


Loading…
Cancel
Save