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.

276 lines
12 KiB

  1. #nullable enable
  2. using IPA.Config.Data;
  3. using IPA.Logging;
  4. using System;
  5. using System.Collections;
  6. using System.Collections.Generic;
  7. using System.Diagnostics;
  8. using System.Linq;
  9. using System.Reflection;
  10. using System.Reflection.Emit;
  11. using System.Runtime.CompilerServices;
  12. using System.Text;
  13. using System.Threading.Tasks;
  14. using Boolean = IPA.Config.Data.Boolean;
  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. #region Logs
  24. private static readonly MethodInfo LogErrorMethod = typeof(GeneratedStoreImpl).GetMethod(nameof(LogError), BindingFlags.NonPublic | BindingFlags.Static);
  25. internal static void LogError(Type? expected, Type? found, string message)
  26. {
  27. Logger.Config.Notice($"{message}{(expected == null ? "" : $" (expected {expected}, found {found?.ToString() ?? "null"})")}");
  28. }
  29. private static readonly MethodInfo LogWarningMethod = typeof(GeneratedStoreImpl).GetMethod(nameof(LogWarning), BindingFlags.NonPublic | BindingFlags.Static);
  30. internal static void LogWarning(string message)
  31. {
  32. Logger.Config.Warn(message);
  33. }
  34. private static readonly MethodInfo LogWarningExceptionMethod = typeof(GeneratedStoreImpl).GetMethod(nameof(LogWarningException), BindingFlags.NonPublic | BindingFlags.Static);
  35. internal static void LogWarningException(Exception exception)
  36. {
  37. Logger.Config.Warn(exception);
  38. }
  39. #endregion
  40. //private delegate LocalBuilder LocalAllocator(Type type, int idx = 0);
  41. private static LocalAllocator MakeLocalAllocator(ILGenerator il)
  42. => new(il);
  43. private struct AllocatedLocal : IDisposable
  44. {
  45. internal readonly LocalAllocator allocator;
  46. public LocalBuilder Local { get; }
  47. public AllocatedLocal(LocalAllocator alloc, LocalBuilder builder)
  48. {
  49. allocator = alloc;
  50. Local = builder;
  51. }
  52. #if NET4
  53. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  54. #endif
  55. public static implicit operator LocalBuilder(AllocatedLocal loc) => loc.Local;
  56. public void Dealloc() => allocator?.Deallocate(this);
  57. public void Dispose() => Dealloc();
  58. }
  59. private sealed class LocalAllocator
  60. {
  61. private readonly ILGenerator ilSource;
  62. private readonly Dictionary<Type, Stack<LocalBuilder>> unallocatedLocals = new();
  63. public LocalAllocator(ILGenerator il)
  64. => ilSource = il;
  65. private Stack<LocalBuilder> GetLocalListForType(Type type)
  66. {
  67. if (!unallocatedLocals.TryGetValue(type, out var list))
  68. unallocatedLocals.Add(type, list = new Stack<LocalBuilder>());
  69. return list;
  70. }
  71. public AllocatedLocal Allocate(Type type)
  72. {
  73. var list = GetLocalListForType(type);
  74. if (list.Count < 1) list.Push(ilSource.DeclareLocal(type));
  75. return new(this, list.Pop());
  76. }
  77. public void Deallocate(AllocatedLocal loc)
  78. {
  79. Debug.Assert(loc.allocator == this);
  80. var list = GetLocalListForType(loc.Local.LocalType);
  81. list.Push(loc.Local);
  82. }
  83. }
  84. private static void EmitLoad(ILGenerator il, SerializedMemberInfo member, Action<ILGenerator> thisarg)
  85. {
  86. thisarg(il); // load this
  87. if (member.IsField)
  88. il.Emit(OpCodes.Ldfld, (FieldInfo)member.Member);
  89. else
  90. { // member is a property
  91. var prop = (PropertyInfo)member.Member;
  92. var getter = prop.GetGetMethod(true);
  93. if (getter is null || getter.IsPrivate)
  94. throw new InvalidOperationException($"Property {member.Name} does not have a getter and is not ignored");
  95. il.Emit(OpCodes.Call, getter);
  96. }
  97. }
  98. private static void EmitStore(ILGenerator il, SerializedMemberInfo member, Action<ILGenerator> value, Action<ILGenerator> thisobj)
  99. {
  100. thisobj(il);
  101. value(il);
  102. if (member.IsField)
  103. il.Emit(OpCodes.Stfld, (FieldInfo)member.Member);
  104. else
  105. { // member is a property
  106. var prop = (PropertyInfo)member.Member;
  107. var setter = prop.GetSetMethod(true);
  108. if (setter is null || setter.IsPrivate)
  109. throw new InvalidOperationException($"Property {member.Name} does not have a setter and is not ignored");
  110. il.Emit(OpCodes.Call, setter);
  111. }
  112. }
  113. private static void EmitWarnException(ILGenerator il, string v)
  114. {
  115. il.Emit(OpCodes.Ldstr, v);
  116. il.Emit(OpCodes.Call, LogWarningMethod);
  117. il.Emit(OpCodes.Call, LogWarningExceptionMethod);
  118. }
  119. private static void EmitLogError(ILGenerator il, string message, bool tailcall = false, Action<ILGenerator>? expected = null, Action<ILGenerator>? found = null)
  120. {
  121. if (expected == null) expected = il => il.Emit(OpCodes.Ldnull);
  122. if (found == null) found = il => il.Emit(OpCodes.Ldnull);
  123. expected(il);
  124. found(il);
  125. il.Emit(OpCodes.Ldstr, message);
  126. if (tailcall) il.Emit(OpCodes.Tailcall);
  127. il.Emit(OpCodes.Call, LogErrorMethod);
  128. }
  129. private static readonly MethodInfo Type_GetTypeFromHandle = typeof(Type).GetMethod(nameof(Type.GetTypeFromHandle));
  130. private static void EmitTypeof(ILGenerator il, Type type)
  131. {
  132. il.Emit(OpCodes.Ldtoken, type);
  133. il.Emit(OpCodes.Call, Type_GetTypeFromHandle);
  134. }
  135. private static readonly Type IDisposable_t = typeof(IDisposable);
  136. private static readonly MethodInfo IDisposable_Dispose = IDisposable_t.GetMethod(nameof(IDisposable.Dispose));
  137. private static readonly Type Decimal_t = typeof(decimal);
  138. private static readonly ConstructorInfo Decimal_FromFloat = Decimal_t.GetConstructor(new[] { typeof(float) });
  139. private static readonly ConstructorInfo Decimal_FromDouble = Decimal_t.GetConstructor(new[] { typeof(double) });
  140. private static readonly ConstructorInfo Decimal_FromInt = Decimal_t.GetConstructor(new[] { typeof(int) });
  141. private static readonly ConstructorInfo Decimal_FromUInt = Decimal_t.GetConstructor(new[] { typeof(uint) });
  142. private static readonly ConstructorInfo Decimal_FromLong = Decimal_t.GetConstructor(new[] { typeof(long) });
  143. private static readonly ConstructorInfo Decimal_FromULong = Decimal_t.GetConstructor(new[] { typeof(ulong) });
  144. private static void EmitNumberConvertTo(ILGenerator il, Type to, Type from)
  145. { // WARNING: THIS USES THE NO-OVERFLOW OPCODES
  146. if (to == from) return;
  147. if (to == Decimal_t)
  148. {
  149. if (from == typeof(float)) il.Emit(OpCodes.Newobj, Decimal_FromFloat);
  150. else if (from == typeof(double)) il.Emit(OpCodes.Newobj, Decimal_FromDouble);
  151. else if (from == typeof(long)) il.Emit(OpCodes.Newobj, Decimal_FromLong);
  152. else if (from == typeof(ulong)) il.Emit(OpCodes.Newobj, Decimal_FromULong);
  153. else if (from == typeof(int)) il.Emit(OpCodes.Newobj, Decimal_FromInt);
  154. else if (from == typeof(uint)) il.Emit(OpCodes.Newobj, Decimal_FromUInt);
  155. else if (from == typeof(IntPtr))
  156. {
  157. EmitNumberConvertTo(il, typeof(long), from);
  158. EmitNumberConvertTo(il, to, typeof(long));
  159. }
  160. else if (from == typeof(UIntPtr))
  161. {
  162. EmitNumberConvertTo(il, typeof(ulong), from);
  163. EmitNumberConvertTo(il, to, typeof(ulong));
  164. }
  165. else
  166. { // if the source is anything else, we first convert to int because that can contain all other values
  167. EmitNumberConvertTo(il, typeof(int), from);
  168. EmitNumberConvertTo(il, to, typeof(int));
  169. };
  170. }
  171. else if (from == Decimal_t)
  172. {
  173. if (to == typeof(IntPtr))
  174. {
  175. EmitNumberConvertTo(il, typeof(long), from);
  176. EmitNumberConvertTo(il, to, typeof(long));
  177. }
  178. else if (to == typeof(UIntPtr))
  179. {
  180. EmitNumberConvertTo(il, typeof(ulong), from);
  181. EmitNumberConvertTo(il, to, typeof(ulong));
  182. }
  183. else
  184. {
  185. var method = Decimal_t.GetMethod($"To{to.Name}"); // conveniently, this is the pattern of the to* names
  186. il.Emit(OpCodes.Call, method);
  187. }
  188. }
  189. else if (to == typeof(IntPtr)) il.Emit(OpCodes.Conv_I);
  190. else if (to == typeof(UIntPtr)) il.Emit(OpCodes.Conv_U);
  191. else if (to == typeof(sbyte)) il.Emit(OpCodes.Conv_I1);
  192. else if (to == typeof(byte)) il.Emit(OpCodes.Conv_U1);
  193. else if (to == typeof(short)) il.Emit(OpCodes.Conv_I2);
  194. else if (to == typeof(ushort)) il.Emit(OpCodes.Conv_U2);
  195. else if (to == typeof(int)) il.Emit(OpCodes.Conv_I4);
  196. else if (to == typeof(uint)) il.Emit(OpCodes.Conv_U4);
  197. else if (to == typeof(long)) il.Emit(OpCodes.Conv_I8);
  198. else if (to == typeof(ulong)) il.Emit(OpCodes.Conv_U8);
  199. else if (to == typeof(float))
  200. {
  201. if (from == typeof(byte)
  202. || from == typeof(ushort)
  203. || from == typeof(uint)
  204. || from == typeof(ulong)
  205. || from == typeof(UIntPtr)) il.Emit(OpCodes.Conv_R_Un);
  206. il.Emit(OpCodes.Conv_R4);
  207. }
  208. else if (to == typeof(double))
  209. {
  210. if (from == typeof(byte)
  211. || from == typeof(ushort)
  212. || from == typeof(uint)
  213. || from == typeof(ulong)
  214. || from == typeof(UIntPtr)) il.Emit(OpCodes.Conv_R_Un);
  215. il.Emit(OpCodes.Conv_R8);
  216. }
  217. }
  218. private static void EmitCreateChildGenerated(ILGenerator il, Type childType, Action<ILGenerator> parentobj)
  219. {
  220. var method = CreateGParent.MakeGenericMethod(childType);
  221. parentobj(il);
  222. il.Emit(OpCodes.Call, method);
  223. }
  224. private static Type GetExpectedValueTypeForType(Type valT)
  225. {
  226. if (typeof(Value).IsAssignableFrom(valT)) // this is a Value subtype
  227. return valT;
  228. if (valT == typeof(string)
  229. || valT == typeof(char)) return typeof(Text);
  230. if (valT == typeof(bool)) return typeof(Boolean);
  231. if (valT == typeof(byte)
  232. || valT == typeof(sbyte)
  233. || valT == typeof(short)
  234. || valT == typeof(ushort)
  235. || valT == typeof(int)
  236. || valT == typeof(uint)
  237. || valT == typeof(long)
  238. || valT == typeof(IntPtr)) return typeof(Integer);
  239. if (valT == typeof(float)
  240. || valT == typeof(double)
  241. || valT == typeof(decimal)
  242. || valT == typeof(ulong) // ulong gets put into this, because decimal can hold it
  243. || valT == typeof(UIntPtr)) return typeof(FloatingPoint);
  244. if (typeof(IEnumerable).IsAssignableFrom(valT)) return typeof(List);
  245. // TODO: fill this out the rest of the way
  246. return typeof(Map); // default for various objects
  247. }
  248. }
  249. }