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.

225 lines
9.8 KiB

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