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.

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