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.

307 lines
14 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.Linq;
  8. using System.Reflection;
  9. using System.Reflection.Emit;
  10. using System.Text;
  11. using System.Threading.Tasks;
  12. using Boolean = IPA.Config.Data.Boolean;
  13. #if NET3
  14. using Net3_Proxy;
  15. using Array = Net3_Proxy.Array;
  16. #endif
  17. namespace IPA.Config.Stores
  18. {
  19. internal static partial class GeneratedStoreImpl
  20. {
  21. private static void EmitDeserializeGeneratedValue(ILGenerator il, SerializedMemberInfo member, Type srcType, LocalAllocator GetLocal,
  22. Action<ILGenerator> thisarg, Action<ILGenerator> parentobj)
  23. {
  24. var IGeneratedStore_Deserialize = typeof(IGeneratedStore).GetMethod(nameof(IGeneratedStore.Deserialize));
  25. using var valuel = GetLocal.Allocate(srcType);
  26. var noCreate = il.DefineLabel();
  27. il.Emit(OpCodes.Stloc, valuel);
  28. EmitLoad(il, member, thisarg);
  29. il.Emit(OpCodes.Dup);
  30. il.Emit(OpCodes.Isinst, typeof(IGeneratedStore));
  31. il.Emit(OpCodes.Brtrue_S, noCreate);
  32. il.Emit(OpCodes.Pop);
  33. EmitCreateChildGenerated(il, member.Type, parentobj);
  34. il.MarkLabel(noCreate);
  35. il.Emit(OpCodes.Dup);
  36. il.Emit(OpCodes.Ldloc, valuel);
  37. il.Emit(OpCodes.Callvirt, IGeneratedStore_Deserialize);
  38. }
  39. private static void EmitDeserializeNullable(ILGenerator il, SerializedMemberInfo member, Type expected, LocalAllocator GetLocal,
  40. Action<ILGenerator> thisarg, Action<ILGenerator> parentobj)
  41. {
  42. if (!member.IsNullable) throw new InvalidOperationException("EmitDeserializeNullable called for non-nullable!");
  43. thisarg ??= il => il.Emit(OpCodes.Ldarg_0);
  44. parentobj ??= thisarg;
  45. EmitDeserializeValue(il, member, member.NullableWrappedType, expected, GetLocal, thisarg, parentobj);
  46. il.Emit(OpCodes.Newobj, member.Nullable_Construct);
  47. }
  48. // top of stack is the Value to deserialize; the type will be as returned from GetExpectedValueTypeForType
  49. // after, top of stack will be thing to write to field
  50. private static void EmitDeserializeValue(ILGenerator il, SerializedMemberInfo member, Type targetType, Type expected, LocalAllocator GetLocal,
  51. Action<ILGenerator> thisarg, Action<ILGenerator> parentobj)
  52. {
  53. if (typeof(Value).IsAssignableFrom(targetType)) return; // do nothing
  54. if (expected == typeof(Text))
  55. {
  56. var getter = expected.GetProperty(nameof(Text.Value)).GetGetMethod();
  57. il.Emit(OpCodes.Call, getter);
  58. if (targetType == typeof(char))
  59. {
  60. var strIndex = typeof(string).GetProperty("Chars").GetGetMethod(); // string's indexer is specially named Chars
  61. il.Emit(OpCodes.Ldc_I4_0);
  62. il.Emit(OpCodes.Call, strIndex);
  63. }
  64. }
  65. else if (expected == typeof(Boolean))
  66. {
  67. var getter = expected.GetProperty(nameof(Boolean.Value)).GetGetMethod();
  68. il.Emit(OpCodes.Call, getter);
  69. }
  70. else if (expected == typeof(Integer))
  71. {
  72. var getter = expected.GetProperty(nameof(Integer.Value)).GetGetMethod();
  73. il.Emit(OpCodes.Call, getter);
  74. EmitNumberConvertTo(il, targetType, getter.ReturnType);
  75. }
  76. else if (expected == typeof(FloatingPoint))
  77. {
  78. var getter = expected.GetProperty(nameof(FloatingPoint.Value)).GetGetMethod();
  79. il.Emit(OpCodes.Call, getter);
  80. EmitNumberConvertTo(il, targetType, getter.ReturnType);
  81. } // TODO: implement stuff for lists and maps of various types (probably call out somewhere else to figure out what to do)
  82. else if (expected == typeof(Map))
  83. {
  84. if (!targetType.IsValueType)
  85. {
  86. EmitDeserializeGeneratedValue(il, member, expected, GetLocal, thisarg, parentobj);
  87. }
  88. else
  89. {
  90. using var mapLocal = GetLocal.Allocate(typeof(Map));
  91. using var resultLocal = GetLocal.Allocate(targetType);
  92. using var valueLocal = GetLocal.Allocate(typeof(Value));
  93. var structure = ReadObjectMembers(targetType);
  94. if (!structure.Any())
  95. {
  96. Logger.Config.Warn($"Custom value type {targetType.FullName} (when compiling serialization of" +
  97. $" {member.Name} on {member.Member.DeclaringType.FullName}) has no accessible members");
  98. il.Emit(OpCodes.Pop);
  99. il.Emit(OpCodes.Ldloca, resultLocal);
  100. il.Emit(OpCodes.Initobj, targetType);
  101. }
  102. else
  103. {
  104. il.Emit(OpCodes.Stloc, mapLocal);
  105. EmitLoad(il, member, thisarg);
  106. il.Emit(OpCodes.Stloc, resultLocal);
  107. EmitDeserializeStructure(il, structure, mapLocal, valueLocal, GetLocal, il => il.Emit(OpCodes.Ldloca, resultLocal), parentobj);
  108. }
  109. il.Emit(OpCodes.Ldloc, resultLocal);
  110. }
  111. }
  112. else
  113. {
  114. Logger.Config.Warn($"Implicit conversions to {expected} are not currently implemented");
  115. il.Emit(OpCodes.Pop);
  116. il.Emit(OpCodes.Ldnull);
  117. }
  118. }
  119. private static void EmitDeserializeStructure(ILGenerator il, IEnumerable<SerializedMemberInfo> structure,
  120. LocalBuilder mapLocal, LocalBuilder valueLocal,
  121. LocalAllocator GetLocal, Action<ILGenerator> thisobj, Action<ILGenerator> parentobj)
  122. {
  123. var Map_TryGetValue = typeof(Map).GetMethod(nameof(Map.TryGetValue));
  124. // head of stack is Map instance
  125. foreach (var mem in structure)
  126. {
  127. var nextLabel = il.DefineLabel();
  128. var endErrorLabel = il.DefineLabel();
  129. il.Emit(OpCodes.Ldloc, mapLocal);
  130. il.Emit(OpCodes.Ldstr, mem.Name);
  131. il.Emit(OpCodes.Ldloca_S, valueLocal);
  132. il.Emit(OpCodes.Call, Map_TryGetValue);
  133. il.Emit(OpCodes.Brtrue_S, endErrorLabel);
  134. EmitLogError(il, $"Missing key {mem.Name}", tailcall: false);
  135. il.Emit(OpCodes.Br, nextLabel);
  136. il.MarkLabel(endErrorLabel);
  137. il.Emit(OpCodes.Ldloc_S, valueLocal);
  138. EmitDeserializeMember(il, mem, nextLabel, il => il.Emit(OpCodes.Ldloc_S, valueLocal), GetLocal, thisobj, parentobj);
  139. il.MarkLabel(nextLabel);
  140. }
  141. }
  142. private static void EmitDeserializeConverter(ILGenerator il, SerializedMemberInfo member, Label nextLabel, LocalAllocator GetLocal,
  143. Action<ILGenerator> thisobj, Action<ILGenerator> parentobj)
  144. {
  145. if (!member.HasConverter)
  146. throw new InvalidOperationException("EmitDeserializeConverter called for member without converter");
  147. using var stlocal = GetLocal.Allocate(typeof(Value));
  148. using var valLocal = GetLocal.Allocate(member.Type);
  149. il.Emit(OpCodes.Stloc, stlocal);
  150. il.BeginExceptionBlock();
  151. il.Emit(OpCodes.Ldsfld, member.ConverterField);
  152. il.Emit(OpCodes.Ldloc, stlocal);
  153. parentobj(il);
  154. if (member.IsGenericConverter)
  155. {
  156. var fromValueBase = member.ConverterBase.GetMethod(nameof(ValueConverter<int>.FromValue),
  157. new[] { typeof(Value), typeof(object) });
  158. var fromValue = member.Converter.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
  159. .FirstOrDefault(m => m.GetBaseDefinition() == fromValueBase) ?? fromValueBase;
  160. il.Emit(OpCodes.Call, fromValue);
  161. }
  162. else
  163. {
  164. var fromValueBase = typeof(IValueConverter).GetMethod(nameof(IValueConverter.FromValue),
  165. new[] { typeof(Value), typeof(object) });
  166. var fromValue = member.Converter.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
  167. .FirstOrDefault(m => m.GetBaseDefinition() == fromValueBase) ?? fromValueBase;
  168. il.Emit(OpCodes.Call, fromValue);
  169. if (member.Type.IsValueType)
  170. il.Emit(OpCodes.Unbox);
  171. }
  172. il.Emit(OpCodes.Stloc, valLocal);
  173. il.BeginCatchBlock(typeof(Exception));
  174. EmitWarnException(il, "Error occurred while deserializing");
  175. il.Emit(OpCodes.Leave, nextLabel);
  176. il.EndExceptionBlock();
  177. il.Emit(OpCodes.Ldloc, valLocal);
  178. }
  179. // emit takes the value being deserialized, logs on error, leaves nothing on stack
  180. private static void EmitDeserializeMember(ILGenerator il, SerializedMemberInfo member, Label nextLabel, Action<ILGenerator> getValue, LocalAllocator GetLocal,
  181. Action<ILGenerator> thisobj, Action<ILGenerator> parentobj)
  182. {
  183. var Object_GetType = typeof(object).GetMethod(nameof(Object.GetType));
  184. var implLabel = il.DefineLabel();
  185. var passedTypeCheck = il.DefineLabel();
  186. var expectType = GetExpectedValueTypeForType(member.ConversionType);
  187. il.Emit(OpCodes.Dup);
  188. il.Emit(OpCodes.Brtrue_S, implLabel); // null check
  189. if (!member.AllowNull)
  190. {
  191. il.Emit(OpCodes.Pop);
  192. EmitLogError(il, $"Member {member.Name} ({member.Type}) not nullable", tailcall: false,
  193. expected: il => EmitTypeof(il, expectType));
  194. il.Emit(OpCodes.Br, nextLabel);
  195. }
  196. else if (member.IsNullable)
  197. {
  198. il.Emit(OpCodes.Pop);
  199. using var valTLocal = GetLocal.Allocate(member.Type);
  200. il.Emit(OpCodes.Ldloca, valTLocal);
  201. il.Emit(OpCodes.Initobj, member.Type);
  202. EmitStore(il, member, il => il.Emit(OpCodes.Ldloc, valTLocal), thisobj);
  203. il.Emit(OpCodes.Br, nextLabel);
  204. }
  205. else
  206. {
  207. il.Emit(OpCodes.Pop);
  208. EmitStore(il, member, il => il.Emit(OpCodes.Ldnull), thisobj);
  209. il.Emit(OpCodes.Br, nextLabel);
  210. }
  211. if (!member.HasConverter)
  212. {
  213. il.MarkLabel(implLabel);
  214. il.Emit(OpCodes.Isinst, expectType); //replaces on stack
  215. il.Emit(OpCodes.Dup); // duplicate cloned value
  216. il.Emit(OpCodes.Brtrue, passedTypeCheck); // null check
  217. }
  218. var errorHandle = il.DefineLabel();
  219. // special cases to handle coersion between Float and Int
  220. if (member.HasConverter)
  221. il.MarkLabel(implLabel);
  222. else if (expectType == typeof(FloatingPoint))
  223. {
  224. var specialTypeCheck = il.DefineLabel();
  225. il.Emit(OpCodes.Pop);
  226. getValue(il);
  227. il.Emit(OpCodes.Isinst, typeof(Integer)); //replaces on stack
  228. il.Emit(OpCodes.Dup); // duplicate cloned value
  229. il.Emit(OpCodes.Brfalse, errorHandle); // null check
  230. var Integer_CoerceToFloat = typeof(Integer).GetMethod(nameof(Integer.AsFloat));
  231. il.Emit(OpCodes.Call, Integer_CoerceToFloat);
  232. il.Emit(OpCodes.Br, passedTypeCheck);
  233. }
  234. else if (expectType == typeof(Integer))
  235. {
  236. var specialTypeCheck = il.DefineLabel();
  237. il.Emit(OpCodes.Pop);
  238. getValue(il);
  239. il.Emit(OpCodes.Isinst, typeof(FloatingPoint)); //replaces on stack
  240. il.Emit(OpCodes.Dup); // duplicate cloned value
  241. il.Emit(OpCodes.Brfalse, errorHandle); // null check
  242. var Float_CoerceToInt = typeof(FloatingPoint).GetMethod(nameof(FloatingPoint.AsInteger));
  243. il.Emit(OpCodes.Call, Float_CoerceToInt);
  244. il.Emit(OpCodes.Br, passedTypeCheck);
  245. }
  246. if (!member.HasConverter)
  247. {
  248. il.MarkLabel(errorHandle);
  249. il.Emit(OpCodes.Pop);
  250. EmitLogError(il, $"Unexpected type deserializing {member.Name}", tailcall: false,
  251. expected: il => EmitTypeof(il, expectType), found: il =>
  252. {
  253. getValue(il);
  254. il.Emit(OpCodes.Callvirt, Object_GetType);
  255. });
  256. il.Emit(OpCodes.Br, nextLabel);
  257. }
  258. il.MarkLabel(passedTypeCheck);
  259. using var local = GetLocal.Allocate(member.Type);
  260. if (member.HasConverter) EmitDeserializeConverter(il, member, nextLabel, GetLocal, thisobj, parentobj);
  261. else if (member.IsNullable) EmitDeserializeNullable(il, member, expectType, GetLocal, thisobj, parentobj);
  262. else EmitDeserializeValue(il, member, member.Type, expectType, GetLocal, thisobj, parentobj);
  263. il.Emit(OpCodes.Stloc, local);
  264. EmitStore(il, member, il => il.Emit(OpCodes.Ldloc, local), thisobj);
  265. }
  266. }
  267. }