You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

181 lines
8.1 KiB

  1. using IPA.Config.Data;
  2. using IPA.Logging;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. using System.Reflection;
  7. using System.Reflection.Emit;
  8. using System.Text;
  9. using System.Threading.Tasks;
  10. using Boolean = IPA.Config.Data.Boolean;
  11. namespace IPA.Config.Stores
  12. {
  13. internal static partial class GeneratedStoreImpl
  14. {
  15. // emit takes no args, leaves Value at top of stack
  16. private static void EmitSerializeMember(ILGenerator il, SerializedMemberInfo member, GetLocal GetLocal, Action<ILGenerator> thisarg = null)
  17. {
  18. EmitLoad(il, member, thisarg);
  19. var endSerialize = il.DefineLabel();
  20. if (member.AllowNull)
  21. {
  22. var passedNull = il.DefineLabel();
  23. il.Emit(OpCodes.Dup);
  24. if (member.IsNullable)
  25. il.Emit(OpCodes.Call, member.Nullable_HasValue.GetGetMethod());
  26. il.Emit(OpCodes.Brtrue, passedNull);
  27. il.Emit(OpCodes.Pop);
  28. il.Emit(OpCodes.Ldnull);
  29. il.Emit(OpCodes.Br, endSerialize);
  30. il.MarkLabel(passedNull);
  31. }
  32. if (member.IsNullable)
  33. il.Emit(OpCodes.Call, member.Nullable_Value.GetGetMethod());
  34. var memberConversionType = member.ConversionType;
  35. var targetType = GetExpectedValueTypeForType(memberConversionType);
  36. if (member.HasConverter)
  37. {
  38. var stlocal = GetLocal(member.Type);
  39. var valLocal = GetLocal(typeof(Value));
  40. il.Emit(OpCodes.Stloc, stlocal);
  41. il.BeginExceptionBlock();
  42. il.Emit(OpCodes.Ldsfld, member.ConverterField);
  43. il.Emit(OpCodes.Ldloc, stlocal);
  44. if (member.IsGenericConverter)
  45. {
  46. var toValueBase = member.ConverterBase.GetMethod(nameof(ValueConverter<int>.ToValue),
  47. new[] { member.ConverterTarget, typeof(object) });
  48. var toValue = member.Converter.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
  49. .FirstOrDefault(m => m.GetBaseDefinition() == toValueBase) ?? toValueBase;
  50. il.Emit(OpCodes.Ldarg_0);
  51. il.Emit(OpCodes.Call, toValue);
  52. }
  53. else
  54. {
  55. var toValueBase = typeof(IValueConverter).GetMethod(nameof(IValueConverter.ToValue),
  56. new[] { typeof(object), typeof(object) });
  57. var toValue = member.Converter.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
  58. .FirstOrDefault(m => m.GetBaseDefinition() == toValueBase) ?? toValueBase;
  59. il.Emit(OpCodes.Box);
  60. il.Emit(OpCodes.Ldarg_0);
  61. il.Emit(OpCodes.Call, toValue);
  62. }
  63. il.Emit(OpCodes.Stloc, valLocal);
  64. il.BeginCatchBlock(typeof(Exception));
  65. EmitWarnException(il, "Error serializing member using converter");
  66. il.Emit(OpCodes.Ldnull);
  67. il.Emit(OpCodes.Stloc, valLocal);
  68. il.EndExceptionBlock();
  69. il.Emit(OpCodes.Ldloc, valLocal);
  70. }
  71. else if (targetType == typeof(Text))
  72. { // only happens when arg is a string or char
  73. var TextCreate = typeof(Value).GetMethod(nameof(Value.Text));
  74. if (member.Type == typeof(char))
  75. {
  76. var strFromChar = typeof(char).GetMethod(nameof(char.ToString), new[] { typeof(char) });
  77. il.Emit(OpCodes.Call, strFromChar);
  78. }
  79. il.Emit(OpCodes.Call, TextCreate);
  80. }
  81. else if (targetType == typeof(Boolean))
  82. {
  83. var BoolCreate = typeof(Value).GetMethod(nameof(Value.Bool));
  84. il.Emit(OpCodes.Call, BoolCreate);
  85. }
  86. else if (targetType == typeof(Integer))
  87. {
  88. var IntCreate = typeof(Value).GetMethod(nameof(Value.Integer));
  89. EmitNumberConvertTo(il, IntCreate.GetParameters()[0].ParameterType, member.Type);
  90. il.Emit(OpCodes.Call, IntCreate);
  91. }
  92. else if (targetType == typeof(FloatingPoint))
  93. {
  94. var FloatCreate = typeof(Value).GetMethod(nameof(Value.Float));
  95. EmitNumberConvertTo(il, FloatCreate.GetParameters()[0].ParameterType, member.Type);
  96. il.Emit(OpCodes.Call, FloatCreate);
  97. }
  98. else if (targetType == typeof(List))
  99. {
  100. // TODO: impl this (enumerables)
  101. il.Emit(OpCodes.Pop);
  102. il.Emit(OpCodes.Ldnull);
  103. }
  104. else if (targetType == typeof(Map))
  105. {
  106. // TODO: support other aggregate types
  107. if (!memberConversionType.IsValueType)
  108. {
  109. // if it is a reference type, we assume that its a generated type implementing IGeneratedStore
  110. var IGeneratedStore_Serialize = typeof(IGeneratedStore).GetMethod(nameof(IGeneratedStore.Serialize));
  111. var IGeneratedStoreT_CopyFrom = typeof(IGeneratedStore<>).MakeGenericType(member.Type)
  112. .GetMethod(nameof(IGeneratedStore<object>.CopyFrom));
  113. if (!member.IsVirtual)
  114. {
  115. var noCreate = il.DefineLabel();
  116. var stlocal = GetLocal(member.Type);
  117. // first check to make sure that this is an IGeneratedStore, because we don't control assignments to it
  118. il.Emit(OpCodes.Dup);
  119. il.Emit(OpCodes.Isinst, typeof(IGeneratedStore));
  120. il.Emit(OpCodes.Brtrue_S, noCreate);
  121. il.Emit(OpCodes.Stloc, stlocal);
  122. EmitCreateChildGenerated(il, member.Type, GetMethodThis);
  123. il.Emit(OpCodes.Dup);
  124. il.Emit(OpCodes.Ldloc, stlocal);
  125. il.Emit(OpCodes.Ldc_I4_0);
  126. il.Emit(OpCodes.Callvirt, IGeneratedStoreT_CopyFrom);
  127. il.Emit(OpCodes.Dup);
  128. il.Emit(OpCodes.Stloc, stlocal);
  129. EmitStore(il, member, il => il.Emit(OpCodes.Ldloc, stlocal), thisarg);
  130. il.MarkLabel(noCreate);
  131. }
  132. il.Emit(OpCodes.Callvirt, IGeneratedStore_Serialize);
  133. }
  134. else
  135. { // generate serialization for value types
  136. var MapCreate = typeof(Value).GetMethod(nameof(Value.Map));
  137. var MapAdd = typeof(Map).GetMethod(nameof(Map.Add));
  138. var valueLocal = GetLocal(memberConversionType);
  139. var structure = ReadObjectMembers(memberConversionType);
  140. if (!structure.Any())
  141. {
  142. Logger.config.Warn($"Custom value type {memberConversionType.FullName} (when compiling serialization of" +
  143. $" {member.Name} on {member.Member.DeclaringType.FullName}) has no accessible members");
  144. il.Emit(OpCodes.Pop);
  145. }
  146. else
  147. {
  148. il.Emit(OpCodes.Stloc, valueLocal);
  149. }
  150. il.Emit(OpCodes.Call, MapCreate);
  151. foreach (var mem in structure)
  152. {
  153. il.Emit(OpCodes.Dup);
  154. il.Emit(OpCodes.Ldstr, mem.Name);
  155. EmitSerializeMember(il, mem, GetLocal, il => il.Emit(OpCodes.Ldloca, valueLocal));
  156. il.Emit(OpCodes.Call, MapAdd);
  157. }
  158. }
  159. }
  160. il.MarkLabel(endSerialize);
  161. }
  162. }
  163. }