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.

155 lines
6.8 KiB

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