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.

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