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.

206 lines
9.4 KiB

  1. using IPA.Logging;
  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. namespace IPA.Config.Stores
  10. {
  11. internal static partial class GeneratedStoreImpl
  12. {
  13. #region Logs
  14. private static readonly MethodInfo LogErrorMethod = typeof(GeneratedStoreImpl).GetMethod(nameof(LogError), BindingFlags.NonPublic | BindingFlags.Static);
  15. internal static void LogError(Type expected, Type found, string message)
  16. {
  17. Logger.config.Notice($"{message}{(expected == null ? "" : $" (expected {expected}, found {found?.ToString() ?? "null"})")}");
  18. }
  19. private static readonly MethodInfo LogWarningMethod = typeof(GeneratedStoreImpl).GetMethod(nameof(LogWarning), BindingFlags.NonPublic | BindingFlags.Static);
  20. internal static void LogWarning(string message)
  21. {
  22. Logger.config.Warn(message);
  23. }
  24. private static readonly MethodInfo LogWarningExceptionMethod = typeof(GeneratedStoreImpl).GetMethod(nameof(LogWarningException), BindingFlags.NonPublic | BindingFlags.Static);
  25. internal static void LogWarningException(Exception exception)
  26. {
  27. Logger.config.Warn(exception);
  28. }
  29. #endregion
  30. private delegate LocalBuilder GetLocal(Type type, int idx = 0);
  31. private static GetLocal MakeGetLocal(ILGenerator il)
  32. { // TODO: improve this shit a bit so that i can release a hold of a variable and do more auto managing
  33. var locals = new List<LocalBuilder>();
  34. LocalBuilder GetLocal(Type ty, int i = 0)
  35. {
  36. var builder = locals.Where(b => b.LocalType == ty).Skip(i).FirstOrDefault();
  37. if (builder == null)
  38. {
  39. builder = il.DeclareLocal(ty);
  40. locals.Add(builder);
  41. }
  42. return builder;
  43. }
  44. return GetLocal;
  45. }
  46. private static void EmitLoad(ILGenerator il, SerializedMemberInfo member, Action<ILGenerator> thisarg = null)
  47. {
  48. if (thisarg == null)
  49. thisarg = il => il.Emit(OpCodes.Ldarg_0);
  50. thisarg(il); // load this
  51. if (member.IsField)
  52. il.Emit(OpCodes.Ldfld, member.Member as FieldInfo);
  53. else
  54. { // member is a property
  55. var prop = member.Member as PropertyInfo;
  56. var getter = prop.GetGetMethod();
  57. if (getter == null) throw new InvalidOperationException($"Property {member.Name} does not have a getter and is not ignored");
  58. il.Emit(OpCodes.Call, getter);
  59. }
  60. }
  61. private static void EmitStore(ILGenerator il, SerializedMemberInfo member, Action<ILGenerator> value)
  62. {
  63. il.Emit(OpCodes.Ldarg_0); // load this
  64. value(il);
  65. if (member.IsField)
  66. il.Emit(OpCodes.Stfld, member.Member as FieldInfo);
  67. else
  68. { // member is a property
  69. var prop = member.Member as PropertyInfo;
  70. var setter = prop.GetSetMethod();
  71. if (setter == null) throw new InvalidOperationException($"Property {member.Name} does not have a setter and is not ignored");
  72. il.Emit(OpCodes.Call, setter);
  73. }
  74. }
  75. private static void EmitWarnException(ILGenerator il, string v)
  76. {
  77. il.Emit(OpCodes.Ldstr, v);
  78. il.Emit(OpCodes.Call, LogWarningMethod);
  79. il.Emit(OpCodes.Call, LogWarningExceptionMethod);
  80. }
  81. private static void EmitLogError(ILGenerator il, string message, bool tailcall = false, Action<ILGenerator> expected = null, Action<ILGenerator> found = null)
  82. {
  83. if (expected == null) expected = il => il.Emit(OpCodes.Ldnull);
  84. if (found == null) found = il => il.Emit(OpCodes.Ldnull);
  85. expected(il);
  86. found(il);
  87. il.Emit(OpCodes.Ldstr, message);
  88. if (tailcall) il.Emit(OpCodes.Tailcall);
  89. il.Emit(OpCodes.Call, LogErrorMethod);
  90. }
  91. private static readonly MethodInfo Type_GetTypeFromHandle = typeof(Type).GetMethod(nameof(Type.GetTypeFromHandle));
  92. private static void EmitTypeof(ILGenerator il, Type type)
  93. {
  94. il.Emit(OpCodes.Ldtoken, type);
  95. il.Emit(OpCodes.Call, Type_GetTypeFromHandle);
  96. }
  97. private static readonly Type IDisposable_t = typeof(IDisposable);
  98. private static readonly MethodInfo IDisposable_Dispose = IDisposable_t.GetMethod(nameof(IDisposable.Dispose));
  99. private static readonly Type Decimal_t = typeof(decimal);
  100. private static readonly ConstructorInfo Decimal_FromFloat = Decimal_t.GetConstructor(new[] { typeof(float) });
  101. private static readonly ConstructorInfo Decimal_FromDouble = Decimal_t.GetConstructor(new[] { typeof(double) });
  102. private static readonly ConstructorInfo Decimal_FromInt = Decimal_t.GetConstructor(new[] { typeof(int) });
  103. private static readonly ConstructorInfo Decimal_FromUInt = Decimal_t.GetConstructor(new[] { typeof(uint) });
  104. private static readonly ConstructorInfo Decimal_FromLong = Decimal_t.GetConstructor(new[] { typeof(long) });
  105. private static readonly ConstructorInfo Decimal_FromULong = Decimal_t.GetConstructor(new[] { typeof(ulong) });
  106. private static void EmitNumberConvertTo(ILGenerator il, Type to, Type from)
  107. { // WARNING: THIS USES THE NO-OVERFLOW OPCODES
  108. if (to == from) return;
  109. if (to == Decimal_t)
  110. {
  111. if (from == typeof(float)) il.Emit(OpCodes.Newobj, Decimal_FromFloat);
  112. else if (from == typeof(double)) il.Emit(OpCodes.Newobj, Decimal_FromDouble);
  113. else if (from == typeof(long)) il.Emit(OpCodes.Newobj, Decimal_FromLong);
  114. else if (from == typeof(ulong)) il.Emit(OpCodes.Newobj, Decimal_FromULong);
  115. else if (from == typeof(int)) il.Emit(OpCodes.Newobj, Decimal_FromInt);
  116. else if (from == typeof(uint)) il.Emit(OpCodes.Newobj, Decimal_FromUInt);
  117. else if (from == typeof(IntPtr))
  118. {
  119. EmitNumberConvertTo(il, typeof(long), from);
  120. EmitNumberConvertTo(il, to, typeof(long));
  121. }
  122. else if (from == typeof(UIntPtr))
  123. {
  124. EmitNumberConvertTo(il, typeof(ulong), from);
  125. EmitNumberConvertTo(il, to, typeof(ulong));
  126. }
  127. else
  128. { // if the source is anything else, we first convert to int because that can contain all other values
  129. EmitNumberConvertTo(il, typeof(int), from);
  130. EmitNumberConvertTo(il, to, typeof(int));
  131. };
  132. }
  133. else if (from == Decimal_t)
  134. {
  135. if (to == typeof(IntPtr))
  136. {
  137. EmitNumberConvertTo(il, typeof(long), from);
  138. EmitNumberConvertTo(il, to, typeof(long));
  139. }
  140. else if (to == typeof(UIntPtr))
  141. {
  142. EmitNumberConvertTo(il, typeof(ulong), from);
  143. EmitNumberConvertTo(il, to, typeof(ulong));
  144. }
  145. else
  146. {
  147. var method = Decimal_t.GetMethod($"To{to.Name}"); // conveniently, this is the pattern of the to* names
  148. il.Emit(OpCodes.Call, method);
  149. }
  150. }
  151. else if (to == typeof(IntPtr)) il.Emit(OpCodes.Conv_I);
  152. else if (to == typeof(UIntPtr)) il.Emit(OpCodes.Conv_U);
  153. else if (to == typeof(sbyte)) il.Emit(OpCodes.Conv_I1);
  154. else if (to == typeof(byte)) il.Emit(OpCodes.Conv_U1);
  155. else if (to == typeof(short)) il.Emit(OpCodes.Conv_I2);
  156. else if (to == typeof(ushort)) il.Emit(OpCodes.Conv_U2);
  157. else if (to == typeof(int)) il.Emit(OpCodes.Conv_I4);
  158. else if (to == typeof(uint)) il.Emit(OpCodes.Conv_U4);
  159. else if (to == typeof(long)) il.Emit(OpCodes.Conv_I8);
  160. else if (to == typeof(ulong)) il.Emit(OpCodes.Conv_U8);
  161. else if (to == typeof(float))
  162. {
  163. if (from == typeof(byte)
  164. || from == typeof(ushort)
  165. || from == typeof(uint)
  166. || from == typeof(ulong)
  167. || from == typeof(UIntPtr)) il.Emit(OpCodes.Conv_R_Un);
  168. il.Emit(OpCodes.Conv_R4);
  169. }
  170. else if (to == typeof(double))
  171. {
  172. if (from == typeof(byte)
  173. || from == typeof(ushort)
  174. || from == typeof(uint)
  175. || from == typeof(ulong)
  176. || from == typeof(UIntPtr)) il.Emit(OpCodes.Conv_R_Un);
  177. il.Emit(OpCodes.Conv_R8);
  178. }
  179. }
  180. private static void EmitCreateChildGenerated(ILGenerator il, Type childType)
  181. {
  182. var method = CreateGParent.MakeGenericMethod(childType);
  183. il.Emit(OpCodes.Ldarg_0);
  184. il.Emit(OpCodes.Call, method);
  185. }
  186. }
  187. }