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.

130 lines
5.0 KiB

  1. #nullable enable
  2. using IPA.Config.Data;
  3. using IPA.Config.Stores.Attributes;
  4. using IPA.Logging;
  5. using System;
  6. using System.Collections.Generic;
  7. using System.ComponentModel;
  8. using System.Linq;
  9. using System.Linq.Expressions;
  10. using System.Reflection;
  11. using System.Reflection.Emit;
  12. using System.Text;
  13. using System.Threading;
  14. using System.Threading.Tasks;
  15. #if NET3
  16. using Net3_Proxy;
  17. using Array = Net3_Proxy.Array;
  18. #endif
  19. namespace IPA.Config.Stores
  20. {
  21. internal static partial class GeneratedStoreImpl
  22. {
  23. private static bool NeedsCorrection(SerializedMemberInfo member)
  24. {
  25. if (member.HasConverter) return false;
  26. var memberType = member.ConversionType;
  27. var expectType = GetExpectedValueTypeForType(memberType);
  28. if (expectType == typeof(Map)) // TODO: make this slightly saner
  29. {
  30. if (expectType.IsValueType)
  31. { // custom value type
  32. return ReadObjectMembers(memberType).Any(NeedsCorrection);
  33. }
  34. return true;
  35. }
  36. return false;
  37. }
  38. // expects start value on stack, exits with final value on stack
  39. private static void EmitCorrectMember(ILGenerator il, SerializedMemberInfo member, bool shouldLock, bool alwaysNew, LocalAllocator GetLocal,
  40. Action<ILGenerator> thisobj, Action<ILGenerator> parentobj)
  41. {
  42. if (!NeedsCorrection(member)) return;
  43. var endLabel = il.DefineLabel();
  44. if (member.IsNullable)
  45. {
  46. il.Emit(OpCodes.Dup);
  47. il.Emit(OpCodes.Call, member.Nullable_HasValue.GetGetMethod());
  48. il.Emit(OpCodes.Brfalse, endLabel);
  49. il.Emit(OpCodes.Call, member.Nullable_Value.GetGetMethod());
  50. }
  51. var convType = member.ConversionType;
  52. if (!convType.IsValueType)
  53. {
  54. // currently the only thing for this is where expect == Map, so do generate shit
  55. var copyFrom = typeof(IGeneratedStore<>).MakeGenericType(convType).GetMethod(nameof(IGeneratedStore<Config>.CopyFrom));
  56. var noCreate = il.DefineLabel();
  57. using var valLocal = GetLocal.Allocate(convType);
  58. if (member.AllowNull)
  59. {
  60. il.Emit(OpCodes.Dup);
  61. il.Emit(OpCodes.Brfalse_S, endLabel); // thing is null, just bypass it all
  62. }
  63. if (!alwaysNew)
  64. {
  65. il.Emit(OpCodes.Dup);
  66. il.Emit(OpCodes.Isinst, typeof(IGeneratedStore));
  67. il.Emit(OpCodes.Brtrue_S, endLabel); // our input is already something we like
  68. }
  69. il.Emit(OpCodes.Stloc, valLocal);
  70. if (!alwaysNew)
  71. {
  72. EmitLoad(il, member, thisobj);
  73. il.Emit(OpCodes.Dup);
  74. il.Emit(OpCodes.Isinst, typeof(IGeneratedStore));
  75. il.Emit(OpCodes.Brtrue_S, noCreate);
  76. il.Emit(OpCodes.Pop);
  77. }
  78. EmitCreateChildGenerated(il, convType, parentobj);
  79. il.MarkLabel(noCreate);
  80. il.Emit(OpCodes.Dup);
  81. il.Emit(OpCodes.Ldloc, valLocal);
  82. il.Emit(shouldLock ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0);
  83. il.Emit(OpCodes.Callvirt, copyFrom);
  84. }
  85. else
  86. {
  87. // for special value types, we'll go ahead and correct each of their members
  88. var structure = ReadObjectMembers(convType);
  89. using var valueLocal = GetLocal.Allocate(convType);
  90. il.Emit(OpCodes.Stloc, valueLocal);
  91. void LdlocaValueLocal(ILGenerator il)
  92. => il.Emit(OpCodes.Ldloca, valueLocal);
  93. foreach (var mem in structure)
  94. {
  95. if (NeedsCorrection(mem))
  96. EmitLoadCorrectStore(il, mem, shouldLock, alwaysNew, GetLocal,
  97. LdlocaValueLocal, LdlocaValueLocal, parentobj);
  98. }
  99. il.Emit(OpCodes.Ldloc, valueLocal);
  100. }
  101. if (member.IsNullable)
  102. il.Emit(OpCodes.Newobj, member.Nullable_Construct);
  103. il.MarkLabel(endLabel);
  104. }
  105. private static void EmitLoadCorrectStore(ILGenerator il, SerializedMemberInfo member, bool shouldLock, bool alwaysNew, LocalAllocator GetLocal,
  106. Action<ILGenerator> loadFrom, Action<ILGenerator> storeTo, Action<ILGenerator> parentobj)
  107. {
  108. EmitStore(il, member, il =>
  109. {
  110. EmitLoad(il, member, loadFrom); // load the member
  111. EmitCorrectMember(il, member, shouldLock, alwaysNew, GetLocal, storeTo, parentobj); // correct it
  112. }, storeTo);
  113. }
  114. }
  115. }