Readers.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. using System;
  2. using System.Collections.Generic;
  3. using Mono.CecilX;
  4. using Mono.CecilX.Cil;
  5. using Mono.CecilX.Rocks;
  6. namespace Mirror.Weaver
  7. {
  8. public static class Readers
  9. {
  10. static Dictionary<TypeReference, MethodReference> readFuncs;
  11. public static void Init()
  12. {
  13. readFuncs = new Dictionary<TypeReference, MethodReference>(new TypeReferenceComparer());
  14. }
  15. internal static void Register(TypeReference dataType, MethodReference methodReference)
  16. {
  17. if (readFuncs.ContainsKey(dataType))
  18. {
  19. // TODO enable this again later.
  20. // Reader has some obsolete functions that were renamed.
  21. // Don't want weaver warnings for all of them.
  22. //Weaver.Warning($"Registering a Read method for {dataType.FullName} when one already exists", methodReference);
  23. }
  24. // we need to import type when we Initialize Readers so import here in case it is used anywhere else
  25. TypeReference imported = Weaver.CurrentAssembly.MainModule.ImportReference(dataType);
  26. readFuncs[imported] = methodReference;
  27. }
  28. static void RegisterReadFunc(TypeReference typeReference, MethodDefinition newReaderFunc)
  29. {
  30. Register(typeReference, newReaderFunc);
  31. Weaver.GeneratedCodeClass.Methods.Add(newReaderFunc);
  32. }
  33. /// <summary>
  34. /// Finds existing reader for type, if non exists trys to create one
  35. /// <para>This method is recursive</para>
  36. /// </summary>
  37. /// <param name="variable"></param>
  38. /// <returns>Returns <see cref="MethodReference"/> or null</returns>
  39. public static MethodReference GetReadFunc(TypeReference variable)
  40. {
  41. if (readFuncs.TryGetValue(variable, out MethodReference foundFunc))
  42. {
  43. return foundFunc;
  44. }
  45. else
  46. {
  47. TypeReference importedVariable = Weaver.CurrentAssembly.MainModule.ImportReference(variable);
  48. return GenerateReader(importedVariable);
  49. }
  50. }
  51. static MethodReference GenerateReader(TypeReference variableReference)
  52. {
  53. // Arrays are special, if we resolve them, we get the element type,
  54. // so the following ifs might choke on it for scriptable objects
  55. // or other objects that require a custom serializer
  56. // thus check if it is an array and skip all the checks.
  57. if (variableReference.IsArray)
  58. {
  59. if (variableReference.IsMultidimensionalArray())
  60. {
  61. Weaver.Error($"{variableReference.Name} is an unsupported type. Multidimensional arrays are not supported", variableReference);
  62. return null;
  63. }
  64. return GenerateReadCollection(variableReference, variableReference.GetElementType(), nameof(NetworkReaderExtensions.ReadArray));
  65. }
  66. TypeDefinition variableDefinition = variableReference.Resolve();
  67. // check if the type is completely invalid
  68. if (variableDefinition == null)
  69. {
  70. Weaver.Error($"{variableReference.Name} is not a supported type", variableReference);
  71. return null;
  72. }
  73. else if (variableReference.IsByReference)
  74. {
  75. // error??
  76. Weaver.Error($"Cannot pass type {variableReference.Name} by reference", variableReference);
  77. return null;
  78. }
  79. // use existing func for known types
  80. if (variableDefinition.IsEnum)
  81. {
  82. return GenerateEnumReadFunc(variableReference);
  83. }
  84. else if (variableDefinition.Is(typeof(ArraySegment<>)))
  85. {
  86. return GenerateArraySegmentReadFunc(variableReference);
  87. }
  88. else if (variableDefinition.Is(typeof(List<>)))
  89. {
  90. GenericInstanceType genericInstance = (GenericInstanceType)variableReference;
  91. TypeReference elementType = genericInstance.GenericArguments[0];
  92. return GenerateReadCollection(variableReference, elementType, nameof(NetworkReaderExtensions.ReadList));
  93. }
  94. else if (variableReference.IsDerivedFrom<NetworkBehaviour>())
  95. {
  96. return GetNetworkBehaviourReader(variableReference);
  97. }
  98. // check if reader generation is applicable on this type
  99. if (variableDefinition.IsDerivedFrom<UnityEngine.Component>())
  100. {
  101. Weaver.Error($"Cannot generate reader for component type {variableReference.Name}. Use a supported type or provide a custom reader", variableReference);
  102. return null;
  103. }
  104. if (variableReference.Is<UnityEngine.Object>())
  105. {
  106. Weaver.Error($"Cannot generate reader for {variableReference.Name}. Use a supported type or provide a custom reader", variableReference);
  107. return null;
  108. }
  109. if (variableReference.Is<UnityEngine.ScriptableObject>())
  110. {
  111. Weaver.Error($"Cannot generate reader for {variableReference.Name}. Use a supported type or provide a custom reader", variableReference);
  112. return null;
  113. }
  114. if (variableDefinition.HasGenericParameters)
  115. {
  116. Weaver.Error($"Cannot generate reader for generic variable {variableReference.Name}. Use a supported type or provide a custom reader", variableReference);
  117. return null;
  118. }
  119. if (variableDefinition.IsInterface)
  120. {
  121. Weaver.Error($"Cannot generate reader for interface {variableReference.Name}. Use a supported type or provide a custom reader", variableReference);
  122. return null;
  123. }
  124. if (variableDefinition.IsAbstract)
  125. {
  126. Weaver.Error($"Cannot generate reader for abstract class {variableReference.Name}. Use a supported type or provide a custom reader", variableReference);
  127. return null;
  128. }
  129. return GenerateClassOrStructReadFunction(variableReference);
  130. }
  131. static MethodReference GetNetworkBehaviourReader(TypeReference variableReference)
  132. {
  133. // uses generic ReadNetworkBehaviour rather than having weaver create one for each NB
  134. MethodReference generic = WeaverTypes.readNetworkBehaviourGeneric;
  135. MethodReference readFunc = generic.MakeGeneric(variableReference);
  136. // register function so it is added to Reader<T>
  137. // use Register instead of RegisterWriteFunc because this is not a generated function
  138. Register(variableReference, readFunc);
  139. return readFunc;
  140. }
  141. static MethodDefinition GenerateEnumReadFunc(TypeReference variable)
  142. {
  143. MethodDefinition readerFunc = GenerateReaderFunction(variable);
  144. ILProcessor worker = readerFunc.Body.GetILProcessor();
  145. worker.Emit(OpCodes.Ldarg_0);
  146. TypeReference underlyingType = variable.Resolve().GetEnumUnderlyingType();
  147. MethodReference underlyingFunc = GetReadFunc(underlyingType);
  148. worker.Emit(OpCodes.Call, underlyingFunc);
  149. worker.Emit(OpCodes.Ret);
  150. return readerFunc;
  151. }
  152. static MethodDefinition GenerateArraySegmentReadFunc(TypeReference variable)
  153. {
  154. GenericInstanceType genericInstance = (GenericInstanceType)variable;
  155. TypeReference elementType = genericInstance.GenericArguments[0];
  156. MethodDefinition readerFunc = GenerateReaderFunction(variable);
  157. ILProcessor worker = readerFunc.Body.GetILProcessor();
  158. // $array = reader.Read<[T]>()
  159. ArrayType arrayType = elementType.MakeArrayType();
  160. worker.Emit(OpCodes.Ldarg_0);
  161. worker.Emit(OpCodes.Call, GetReadFunc(arrayType));
  162. // return new ArraySegment<T>($array);
  163. worker.Emit(OpCodes.Newobj, WeaverTypes.ArraySegmentConstructorReference.MakeHostInstanceGeneric(genericInstance));
  164. worker.Emit(OpCodes.Ret);
  165. return readerFunc;
  166. }
  167. static MethodDefinition GenerateReaderFunction(TypeReference variable)
  168. {
  169. string functionName = "_Read_" + variable.FullName;
  170. // create new reader for this type
  171. MethodDefinition readerFunc = new MethodDefinition(functionName,
  172. MethodAttributes.Public |
  173. MethodAttributes.Static |
  174. MethodAttributes.HideBySig,
  175. variable);
  176. readerFunc.Parameters.Add(new ParameterDefinition("reader", ParameterAttributes.None, WeaverTypes.Import<NetworkReader>()));
  177. readerFunc.Body.InitLocals = true;
  178. RegisterReadFunc(variable, readerFunc);
  179. return readerFunc;
  180. }
  181. static MethodDefinition GenerateReadCollection(TypeReference variable, TypeReference elementType, string readerFunction)
  182. {
  183. MethodDefinition readerFunc = GenerateReaderFunction(variable);
  184. // generate readers for the element
  185. GetReadFunc(elementType);
  186. ModuleDefinition module = Weaver.CurrentAssembly.MainModule;
  187. TypeReference readerExtensions = module.ImportReference(typeof(NetworkReaderExtensions));
  188. MethodReference listReader = Resolvers.ResolveMethod(readerExtensions, Weaver.CurrentAssembly, readerFunction);
  189. GenericInstanceMethod methodRef = new GenericInstanceMethod(listReader);
  190. methodRef.GenericArguments.Add(elementType);
  191. // generates
  192. // return reader.ReadList<T>();
  193. ILProcessor worker = readerFunc.Body.GetILProcessor();
  194. worker.Emit(OpCodes.Ldarg_0); // reader
  195. worker.Emit(OpCodes.Call, methodRef); // Read
  196. worker.Emit(OpCodes.Ret);
  197. return readerFunc;
  198. }
  199. static MethodDefinition GenerateClassOrStructReadFunction(TypeReference variable)
  200. {
  201. MethodDefinition readerFunc = GenerateReaderFunction(variable);
  202. // create local for return value
  203. readerFunc.Body.Variables.Add(new VariableDefinition(variable));
  204. ILProcessor worker = readerFunc.Body.GetILProcessor();
  205. TypeDefinition td = variable.Resolve();
  206. if (!td.IsValueType)
  207. GenerateNullCheck(worker);
  208. CreateNew(variable, worker, td);
  209. ReadAllFields(variable, worker);
  210. worker.Emit(OpCodes.Ldloc_0);
  211. worker.Emit(OpCodes.Ret);
  212. return readerFunc;
  213. }
  214. static void GenerateNullCheck(ILProcessor worker)
  215. {
  216. // if (!reader.ReadBoolean()) {
  217. // return null;
  218. // }
  219. worker.Emit(OpCodes.Ldarg_0);
  220. worker.Emit(OpCodes.Call, GetReadFunc(WeaverTypes.Import<bool>()));
  221. Instruction labelEmptyArray = worker.Create(OpCodes.Nop);
  222. worker.Emit(OpCodes.Brtrue, labelEmptyArray);
  223. // return null
  224. worker.Emit(OpCodes.Ldnull);
  225. worker.Emit(OpCodes.Ret);
  226. worker.Append(labelEmptyArray);
  227. }
  228. // Initialize the local variable with a new instance
  229. static void CreateNew(TypeReference variable, ILProcessor worker, TypeDefinition td)
  230. {
  231. if (variable.IsValueType)
  232. {
  233. // structs are created with Initobj
  234. worker.Emit(OpCodes.Ldloca, 0);
  235. worker.Emit(OpCodes.Initobj, variable);
  236. }
  237. else if (td.IsDerivedFrom<UnityEngine.ScriptableObject>())
  238. {
  239. GenericInstanceMethod genericInstanceMethod = new GenericInstanceMethod(WeaverTypes.ScriptableObjectCreateInstanceMethod);
  240. genericInstanceMethod.GenericArguments.Add(variable);
  241. worker.Emit(OpCodes.Call, genericInstanceMethod);
  242. worker.Emit(OpCodes.Stloc_0);
  243. }
  244. else
  245. {
  246. // classes are created with their constructor
  247. MethodDefinition ctor = Resolvers.ResolveDefaultPublicCtor(variable);
  248. if (ctor == null)
  249. {
  250. Weaver.Error($"{variable.Name} can't be deserialized because it has no default constructor", variable);
  251. return;
  252. }
  253. MethodReference ctorRef = Weaver.CurrentAssembly.MainModule.ImportReference(ctor);
  254. worker.Emit(OpCodes.Newobj, ctorRef);
  255. worker.Emit(OpCodes.Stloc_0);
  256. }
  257. }
  258. static void ReadAllFields(TypeReference variable, ILProcessor worker)
  259. {
  260. foreach (FieldDefinition field in variable.FindAllPublicFields())
  261. {
  262. // mismatched ldloca/ldloc for struct/class combinations is invalid IL, which causes crash at runtime
  263. OpCode opcode = variable.IsValueType ? OpCodes.Ldloca : OpCodes.Ldloc;
  264. worker.Emit(opcode, 0);
  265. MethodReference readFunc = GetReadFunc(field.FieldType);
  266. if (readFunc != null)
  267. {
  268. worker.Emit(OpCodes.Ldarg_0);
  269. worker.Emit(OpCodes.Call, readFunc);
  270. }
  271. else
  272. {
  273. Weaver.Error($"{field.Name} has an unsupported type", field);
  274. }
  275. FieldReference fieldRef = Weaver.CurrentAssembly.MainModule.ImportReference(field);
  276. worker.Emit(OpCodes.Stfld, fieldRef);
  277. }
  278. }
  279. /// <summary>
  280. /// Save a delegate for each one of the readers into <see cref="Reader{T}.read"/>
  281. /// </summary>
  282. /// <param name="worker"></param>
  283. internal static void InitializeReaders(ILProcessor worker)
  284. {
  285. ModuleDefinition module = Weaver.CurrentAssembly.MainModule;
  286. TypeReference genericReaderClassRef = module.ImportReference(typeof(Reader<>));
  287. System.Reflection.FieldInfo fieldInfo = typeof(Reader<>).GetField(nameof(Reader<object>.read));
  288. FieldReference fieldRef = module.ImportReference(fieldInfo);
  289. TypeReference networkReaderRef = module.ImportReference(typeof(NetworkReader));
  290. TypeReference funcRef = module.ImportReference(typeof(Func<,>));
  291. MethodReference funcConstructorRef = module.ImportReference(typeof(Func<,>).GetConstructors()[0]);
  292. foreach (KeyValuePair<TypeReference, MethodReference> kvp in readFuncs)
  293. {
  294. TypeReference targetType = kvp.Key;
  295. MethodReference readFunc = kvp.Value;
  296. // create a Func<NetworkReader, T> delegate
  297. worker.Emit(OpCodes.Ldnull);
  298. worker.Emit(OpCodes.Ldftn, readFunc);
  299. GenericInstanceType funcGenericInstance = funcRef.MakeGenericInstanceType(networkReaderRef, targetType);
  300. MethodReference funcConstructorInstance = funcConstructorRef.MakeHostInstanceGeneric(funcGenericInstance);
  301. worker.Emit(OpCodes.Newobj, funcConstructorInstance);
  302. // save it in Reader<T>.read
  303. GenericInstanceType genericInstance = genericReaderClassRef.MakeGenericInstanceType(targetType);
  304. FieldReference specializedField = fieldRef.SpecializeField(genericInstance);
  305. worker.Emit(OpCodes.Stsfld, specializedField);
  306. }
  307. }
  308. }
  309. }