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.

237 lines
11 KiB

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