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.

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