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.

131 lines
5.1 KiB

1 year ago
  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. // TODO: when we have a nullable, we need to save to a local to call methods
  45. if (member.IsNullable)
  46. {
  47. il.Emit(OpCodes.Dup);
  48. il.Emit(OpCodes.Call, member.Nullable_HasValue.GetGetMethod());
  49. il.Emit(OpCodes.Brfalse, endLabel);
  50. il.Emit(OpCodes.Call, member.Nullable_Value.GetGetMethod());
  51. }
  52. var convType = member.ConversionType;
  53. if (!convType.IsValueType)
  54. {
  55. // currently the only thing for this is where expect == Map, so do generate shit
  56. var copyFrom = typeof(IGeneratedStore<>).MakeGenericType(convType).GetMethod(nameof(IGeneratedStore<Config>.CopyFrom));
  57. var noCreate = il.DefineLabel();
  58. using var valLocal = GetLocal.Allocate(convType);
  59. if (member.AllowNull)
  60. {
  61. il.Emit(OpCodes.Dup);
  62. il.Emit(OpCodes.Brfalse_S, endLabel); // thing is null, just bypass it all
  63. }
  64. if (!alwaysNew)
  65. {
  66. il.Emit(OpCodes.Dup);
  67. il.Emit(OpCodes.Isinst, typeof(IGeneratedStore));
  68. il.Emit(OpCodes.Brtrue_S, endLabel); // our input is already something we like
  69. }
  70. il.Emit(OpCodes.Stloc, valLocal);
  71. if (!alwaysNew)
  72. {
  73. EmitLoad(il, member, thisobj);
  74. il.Emit(OpCodes.Dup);
  75. il.Emit(OpCodes.Isinst, typeof(IGeneratedStore));
  76. il.Emit(OpCodes.Brtrue_S, noCreate);
  77. il.Emit(OpCodes.Pop);
  78. }
  79. EmitCreateChildGenerated(il, convType, parentobj);
  80. il.MarkLabel(noCreate);
  81. il.Emit(OpCodes.Dup);
  82. il.Emit(OpCodes.Ldloc, valLocal);
  83. il.Emit(shouldLock ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0);
  84. il.Emit(OpCodes.Callvirt, copyFrom);
  85. }
  86. else
  87. {
  88. // for special value types, we'll go ahead and correct each of their members
  89. var structure = ReadObjectMembers(convType);
  90. using var valueLocal = GetLocal.Allocate(convType);
  91. il.Emit(OpCodes.Stloc, valueLocal);
  92. void LdlocaValueLocal(ILGenerator il)
  93. => il.Emit(OpCodes.Ldloca, valueLocal);
  94. foreach (var mem in structure)
  95. {
  96. if (NeedsCorrection(mem))
  97. EmitLoadCorrectStore(il, mem, shouldLock, alwaysNew, GetLocal,
  98. LdlocaValueLocal, LdlocaValueLocal, parentobj);
  99. }
  100. il.Emit(OpCodes.Ldloc, valueLocal);
  101. }
  102. if (member.IsNullable)
  103. il.Emit(OpCodes.Newobj, member.Nullable_Construct);
  104. il.MarkLabel(endLabel);
  105. }
  106. private static void EmitLoadCorrectStore(ILGenerator il, SerializedMemberInfo member, bool shouldLock, bool alwaysNew, LocalAllocator GetLocal,
  107. Action<ILGenerator> loadFrom, Action<ILGenerator> storeTo, Action<ILGenerator> parentobj)
  108. {
  109. EmitStore(il, member, il =>
  110. {
  111. EmitLoad(il, member, loadFrom); // load the member
  112. EmitCorrectMember(il, member, shouldLock, alwaysNew, GetLocal, storeTo, parentobj); // correct it
  113. }, storeTo);
  114. }
  115. }
  116. }