1413 lines
		
	
	
		
			61 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			1413 lines
		
	
	
		
			61 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| using FishNet.CodeGenerating.Extension;
 | |
| using FishNet.CodeGenerating.Helping.Extension;
 | |
| using FishNet.CodeGenerating.ILCore;
 | |
| using FishNet.Managing;
 | |
| using FishNet.Managing.Logging;
 | |
| using FishNet.Object;
 | |
| using FishNet.Object.Helping;
 | |
| using FishNet.Serializing;
 | |
| using FishNet.Serializing.Helping;
 | |
| using MonoFN.Cecil;
 | |
| using MonoFN.Cecil.Cil;
 | |
| using MonoFN.Cecil.Rocks;
 | |
| using System;
 | |
| using System.Collections.Generic;
 | |
| using UnityEngine;
 | |
| using SR = System.Reflection;
 | |
| 
 | |
| namespace FishNet.CodeGenerating.Helping
 | |
| {
 | |
|     internal class GeneralHelper : CodegenBase
 | |
|     {
 | |
|         #region Reflection references.
 | |
|         public string CodegenExcludeAttribute_FullName;
 | |
|         public string CodegenIncludeAttribute_FullName;
 | |
|         public MethodReference Extension_Attribute_Ctor_MethodRef;
 | |
|         public MethodReference Queue_Enqueue_MethodRef;
 | |
|         public MethodReference Queue_get_Count_MethodRef;
 | |
|         public MethodReference Queue_Dequeue_MethodRef;
 | |
|         public MethodReference Queue_Clear_MethodRef;
 | |
|         public TypeReference List_TypeRef;
 | |
|         public MethodReference List_Clear_MethodRef;
 | |
|         public MethodReference List_get_Item_MethodRef;
 | |
|         public MethodReference List_get_Count_MethodRef;
 | |
|         public MethodReference List_Add_MethodRef;
 | |
|         public MethodReference List_RemoveRange_MethodRef;
 | |
|         public MethodReference InstanceFinder_NetworkManager_MethodRef;
 | |
|         public MethodReference NetworkBehaviour_CanLog_MethodRef;
 | |
|         public MethodReference NetworkBehaviour_NetworkManager_MethodRef;
 | |
|         public MethodReference NetworkManager_LogCommon_MethodRef;
 | |
|         public MethodReference NetworkManager_LogWarning_MethodRef;
 | |
|         public MethodReference NetworkManager_LogError_MethodRef;
 | |
|         public MethodReference Debug_LogCommon_MethodRef;
 | |
|         public MethodReference Debug_LogWarning_MethodRef;
 | |
|         public MethodReference Debug_LogError_MethodRef;
 | |
|         public MethodReference IsServer_MethodRef;
 | |
|         public MethodReference IsClient_MethodRef;
 | |
|         public MethodReference NetworkObject_Deinitializing_MethodRef;
 | |
|         public MethodReference Application_IsPlaying_MethodRef;
 | |
|         public string NonSerialized_Attribute_FullName;
 | |
|         public string Single_FullName;
 | |
|         public TypeReference FunctionT2TypeRef;
 | |
|         public TypeReference FunctionT3TypeRef;
 | |
|         public MethodReference FunctionT2ConstructorMethodRef;
 | |
|         public MethodReference FunctionT3ConstructorMethodRef;
 | |
|         //GeneratedComparer
 | |
|         public MethodReference GeneratedComparer_Compare_Set_MethodRef;
 | |
|         public MethodReference GeneratedComparer_IsDefault_Set_MethodRef;
 | |
|         public TypeReference GeneratedComparer_TypeRef;
 | |
|         public TypeDefinition GeneratedComparer_ClassTypeDef;
 | |
|         public MethodDefinition GeneratedComparer_OnLoadMethodDef;
 | |
|         public TypeReference IEquatable_TypeRef;
 | |
|         //Actions.
 | |
|         public TypeReference ActionT2_TypeRef;
 | |
|         public TypeReference ActionT3_TypeRef;
 | |
|         public MethodReference ActionT2Constructor_MethodRef;
 | |
|         public MethodReference ActionT3Constructor_MethodRef;
 | |
| 
 | |
|         private Dictionary<Type, TypeReference> _importedTypeReferences = new Dictionary<Type, TypeReference>();
 | |
|         private Dictionary<FieldDefinition, FieldReference> _importedFieldReferences = new Dictionary<FieldDefinition, FieldReference>();
 | |
|         private Dictionary<MethodReference, MethodDefinition> _methodReferenceResolves = new Dictionary<MethodReference, MethodDefinition>();
 | |
|         private Dictionary<TypeReference, TypeDefinition> _typeReferenceResolves = new Dictionary<TypeReference, TypeDefinition>();
 | |
|         private Dictionary<FieldReference, FieldDefinition> _fieldReferenceResolves = new Dictionary<FieldReference, FieldDefinition>();
 | |
|         private Dictionary<string, MethodDefinition> _comparerDelegates = new Dictionary<string, MethodDefinition>();
 | |
|         #endregion
 | |
| 
 | |
|         #region Const.
 | |
|         public const string UNITYENGINE_ASSEMBLY_PREFIX = "UnityEngine.";
 | |
|         #endregion
 | |
| 
 | |
|         public override bool ImportReferences()
 | |
|         {
 | |
|             Type tmpType;
 | |
|             TypeReference tmpTr;
 | |
|             SR.MethodInfo tmpMi;
 | |
|             SR.PropertyInfo tmpPi;
 | |
| 
 | |
|             NonSerialized_Attribute_FullName = typeof(NonSerializedAttribute).FullName;
 | |
|             Single_FullName = typeof(float).FullName;
 | |
| 
 | |
|             ActionT2_TypeRef = base.ImportReference(typeof(Action<,>));
 | |
|             ActionT3_TypeRef = base.ImportReference(typeof(Action<,,>));
 | |
|             ActionT2Constructor_MethodRef = base.ImportReference(typeof(Action<,>).GetConstructors()[0]);
 | |
|             ActionT3Constructor_MethodRef = base.ImportReference(typeof(Action<,,>).GetConstructors()[0]);
 | |
| 
 | |
|             CodegenExcludeAttribute_FullName = typeof(CodegenExcludeAttribute).FullName;
 | |
|             CodegenIncludeAttribute_FullName = typeof(CodegenIncludeAttribute).FullName;
 | |
| 
 | |
|             tmpType = typeof(Queue<>);
 | |
|             base.ImportReference(tmpType);
 | |
|             tmpMi = tmpType.GetMethod("get_Count");
 | |
|             Queue_get_Count_MethodRef = base.ImportReference(tmpMi);
 | |
|             foreach (SR.MethodInfo mi in tmpType.GetMethods())
 | |
|             {
 | |
|                 if (mi.Name == nameof(Queue<int>.Enqueue))
 | |
|                     Queue_Enqueue_MethodRef = base.ImportReference(mi);
 | |
|                 else if (mi.Name == nameof(Queue<int>.Dequeue))
 | |
|                     Queue_Dequeue_MethodRef = base.ImportReference(mi);
 | |
|                 else if (mi.Name == nameof(Queue<int>.Clear))
 | |
|                     Queue_Clear_MethodRef = base.ImportReference(mi);
 | |
|             }
 | |
| 
 | |
|             /* MISC */
 | |
|             //
 | |
|             tmpType = typeof(UnityEngine.Application);
 | |
|             tmpPi = tmpType.GetProperty(nameof(UnityEngine.Application.isPlaying));
 | |
|             if (tmpPi != null)
 | |
|                 Application_IsPlaying_MethodRef = base.ImportReference(tmpPi.GetMethod);
 | |
|             //
 | |
|             tmpType = typeof(System.Runtime.CompilerServices.ExtensionAttribute);
 | |
|             tmpTr = base.ImportReference(tmpType);
 | |
|             Extension_Attribute_Ctor_MethodRef = base.ImportReference(tmpTr.GetConstructor(base.Session));
 | |
| 
 | |
|             //Networkbehaviour.
 | |
|             Type networkBehaviourType = typeof(NetworkBehaviour);
 | |
|             foreach (SR.MethodInfo methodInfo in networkBehaviourType.GetMethods())
 | |
|             {
 | |
|                 if (methodInfo.Name == nameof(NetworkBehaviour.CanLog))
 | |
|                     NetworkBehaviour_CanLog_MethodRef = base.ImportReference(methodInfo);
 | |
|             }
 | |
|             foreach (SR.PropertyInfo propertyInfo in networkBehaviourType.GetProperties())
 | |
|             {
 | |
|                 if (propertyInfo.Name == nameof(NetworkBehaviour.NetworkManager))
 | |
|                     NetworkBehaviour_NetworkManager_MethodRef = base.ImportReference(propertyInfo.GetMethod);
 | |
|             }
 | |
| 
 | |
|             //Instancefinder.
 | |
|             Type instanceFinderType = typeof(InstanceFinder);
 | |
|             SR.PropertyInfo getNetworkManagerPropertyInfo = instanceFinderType.GetProperty(nameof(InstanceFinder.NetworkManager));
 | |
|             InstanceFinder_NetworkManager_MethodRef = base.ImportReference(getNetworkManagerPropertyInfo.GetMethod);
 | |
| 
 | |
|             //NetworkManager debug logs. 
 | |
|             Type networkManagerType = typeof(NetworkManager);
 | |
|             foreach (SR.MethodInfo methodInfo in networkManagerType.GetMethods())
 | |
|             {
 | |
|                 if (methodInfo.Name == nameof(NetworkManager.Log) && methodInfo.GetParameters().Length == 1)
 | |
|                     NetworkManager_LogCommon_MethodRef = base.ImportReference(methodInfo);
 | |
|                 else if (methodInfo.Name == nameof(NetworkManager.LogWarning))
 | |
|                     NetworkManager_LogWarning_MethodRef = base.ImportReference(methodInfo);
 | |
|                 else if (methodInfo.Name == nameof(NetworkManager.LogError))
 | |
|                     NetworkManager_LogError_MethodRef = base.ImportReference(methodInfo);
 | |
|             }
 | |
| 
 | |
|             //Lists.
 | |
|             tmpType = typeof(List<>);
 | |
|             List_TypeRef = base.ImportReference(tmpType);
 | |
|             SR.MethodInfo lstMi;
 | |
|             lstMi = tmpType.GetMethod("Add");
 | |
|             List_Add_MethodRef = base.ImportReference(lstMi);
 | |
|             lstMi = tmpType.GetMethod("RemoveRange");
 | |
|             List_RemoveRange_MethodRef = base.ImportReference(lstMi);
 | |
|             lstMi = tmpType.GetMethod("get_Count");
 | |
|             List_get_Count_MethodRef = base.ImportReference(lstMi);
 | |
|             lstMi = tmpType.GetMethod("get_Item");
 | |
|             List_get_Item_MethodRef = base.ImportReference(lstMi);
 | |
|             lstMi = tmpType.GetMethod("Clear");
 | |
|             List_Clear_MethodRef = base.ImportReference(lstMi);
 | |
| 
 | |
|             //Unity debug logs.
 | |
|             Type debugType = typeof(UnityEngine.Debug);
 | |
|             foreach (SR.MethodInfo methodInfo in debugType.GetMethods())
 | |
|             {
 | |
|                 if (methodInfo.Name == nameof(Debug.LogWarning) && methodInfo.GetParameters().Length == 1)
 | |
|                     Debug_LogWarning_MethodRef = base.ImportReference(methodInfo);
 | |
|                 else if (methodInfo.Name == nameof(Debug.LogError) && methodInfo.GetParameters().Length == 1)
 | |
|                     Debug_LogError_MethodRef = base.ImportReference(methodInfo);
 | |
|                 else if (methodInfo.Name == nameof(Debug.Log) && methodInfo.GetParameters().Length == 1)
 | |
|                     Debug_LogCommon_MethodRef = base.ImportReference(methodInfo);
 | |
|             }
 | |
| 
 | |
|             Type codegenHelper = typeof(CodegenHelper);
 | |
|             foreach (SR.MethodInfo methodInfo in codegenHelper.GetMethods())
 | |
|             {
 | |
|                 if (methodInfo.Name == nameof(CodegenHelper.NetworkObject_Deinitializing))
 | |
|                     NetworkObject_Deinitializing_MethodRef = base.ImportReference(methodInfo);
 | |
|                 else if (methodInfo.Name == nameof(CodegenHelper.IsClient))
 | |
|                     IsClient_MethodRef = base.ImportReference(methodInfo);
 | |
|                 else if (methodInfo.Name == nameof(CodegenHelper.IsServer))
 | |
|                     IsServer_MethodRef = base.ImportReference(methodInfo);
 | |
|             }
 | |
| 
 | |
|             //Generic functions.
 | |
|             FunctionT2TypeRef = base.ImportReference(typeof(Func<,>));
 | |
|             FunctionT3TypeRef = base.ImportReference(typeof(Func<,,>));
 | |
|             FunctionT2ConstructorMethodRef = base.ImportReference(typeof(Func<,>).GetConstructors()[0]);
 | |
|             FunctionT3ConstructorMethodRef = base.ImportReference(typeof(Func<,,>).GetConstructors()[0]);
 | |
| 
 | |
|             GeneratedComparers();
 | |
| 
 | |
|             //Sets up for generated comparers.
 | |
|             void GeneratedComparers()
 | |
|             {
 | |
|                 GeneralHelper gh = base.GetClass<GeneralHelper>();
 | |
|                 GeneratedComparer_ClassTypeDef = gh.GetOrCreateClass(out _, WriterProcessor.GENERATED_TYPE_ATTRIBUTES, "GeneratedComparers___Internal", null, WriterProcessor.GENERATED_WRITER_NAMESPACE);
 | |
|                 bool created;
 | |
|                 GeneratedComparer_OnLoadMethodDef = gh.GetOrCreateMethod(GeneratedComparer_ClassTypeDef, out created, WriterProcessor.INITIALIZEONCE_METHOD_ATTRIBUTES, WriterProcessor.INITIALIZEONCE_METHOD_NAME, base.Module.TypeSystem.Void);
 | |
|                 if (created)
 | |
|                 {
 | |
|                     gh.CreateRuntimeInitializeOnLoadMethodAttribute(GeneratedComparer_OnLoadMethodDef);
 | |
|                     GeneratedComparer_OnLoadMethodDef.Body.GetILProcessor().Emit(OpCodes.Ret);
 | |
|                 }
 | |
| 
 | |
|                 System.Type repComparerType = typeof(GeneratedComparer<>);
 | |
|                 GeneratedComparer_TypeRef = base.ImportReference(repComparerType);
 | |
|                 System.Reflection.PropertyInfo pi;
 | |
|                 pi = repComparerType.GetProperty(nameof(GeneratedComparer<int>.Compare));
 | |
|                 GeneratedComparer_Compare_Set_MethodRef = base.ImportReference(pi.GetSetMethod());
 | |
|                 pi = repComparerType.GetProperty(nameof(GeneratedComparer<int>.IsDefault));
 | |
|                 GeneratedComparer_IsDefault_Set_MethodRef = base.ImportReference(pi.GetSetMethod());
 | |
| 
 | |
|                 System.Type iEquatableType = typeof(IEquatable<>);
 | |
|                 IEquatable_TypeRef = base.ImportReference(iEquatableType);
 | |
|             }
 | |
| 
 | |
|             return true;
 | |
|         }
 | |
| 
 | |
| 
 | |
| 
 | |
|         #region Resolves.
 | |
|         /// <summary>
 | |
|         /// Adds a typeRef to TypeReferenceResolves.
 | |
|         /// </summary>
 | |
|         public void AddTypeReferenceResolve(TypeReference typeRef, TypeDefinition typeDef)
 | |
|         {
 | |
|             _typeReferenceResolves[typeRef] = typeDef;
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Gets a TypeDefinition for typeRef.
 | |
|         /// </summary>
 | |
|         public TypeDefinition GetTypeReferenceResolve(TypeReference typeRef)
 | |
|         {
 | |
|             TypeDefinition result;
 | |
|             if (_typeReferenceResolves.TryGetValue(typeRef, out result))
 | |
|             {
 | |
|                 return result;
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 result = typeRef.Resolve();
 | |
|                 AddTypeReferenceResolve(typeRef, result);
 | |
|             }
 | |
| 
 | |
|             return result;
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Adds a methodRef to MethodReferenceResolves.
 | |
|         /// </summary>
 | |
|         public void AddMethodReferenceResolve(MethodReference methodRef, MethodDefinition methodDef)
 | |
|         {
 | |
|             _methodReferenceResolves[methodRef] = methodDef;
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Gets a TypeDefinition for typeRef.
 | |
|         /// </summary>
 | |
|         public MethodDefinition GetMethodReferenceResolve(MethodReference methodRef)
 | |
|         {
 | |
|             MethodDefinition result;
 | |
|             if (_methodReferenceResolves.TryGetValue(methodRef, out result))
 | |
|             {
 | |
|                 return result;
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 result = methodRef.Resolve();
 | |
|                 AddMethodReferenceResolve(methodRef, result);
 | |
|             }
 | |
| 
 | |
|             return result;
 | |
|         }
 | |
| 
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Adds a fieldRef to FieldReferenceResolves.
 | |
|         /// </summary>
 | |
|         public void AddFieldReferenceResolve(FieldReference fieldRef, FieldDefinition fieldDef)
 | |
|         {
 | |
|             _fieldReferenceResolves[fieldRef] = fieldDef;
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Gets a FieldDefinition for fieldRef.
 | |
|         /// </summary>
 | |
|         public FieldDefinition GetFieldReferenceResolve(FieldReference fieldRef)
 | |
|         {
 | |
|             FieldDefinition result;
 | |
|             if (_fieldReferenceResolves.TryGetValue(fieldRef, out result))
 | |
|             {
 | |
|                 return result;
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 result = fieldRef.Resolve();
 | |
|                 AddFieldReferenceResolve(fieldRef, result);
 | |
|             }
 | |
| 
 | |
|             return result;
 | |
|         }
 | |
|         #endregion
 | |
| 
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Makes a method an extension method.
 | |
|         /// </summary>
 | |
|         public void MakeExtensionMethod(MethodDefinition md)
 | |
|         {
 | |
|             if (md.Parameters.Count == 0)
 | |
|             {
 | |
|                 base.LogError($"Method {md.FullName} cannot be made an extension method because it has no parameters.");
 | |
|                 return;
 | |
|             }
 | |
| 
 | |
|             md.Attributes |= (MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig);
 | |
|             CustomAttribute ca = new CustomAttribute(Extension_Attribute_Ctor_MethodRef);
 | |
|             md.CustomAttributes.Add(ca);
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Returns if typeDef should be ignored.
 | |
|         /// </summary>
 | |
|         /// <param name="typeDef"></param>
 | |
|         /// <returns></returns>
 | |
|         public bool IgnoreTypeDefinition(TypeDefinition typeDef)
 | |
|         {
 | |
|             foreach (CustomAttribute item in typeDef.CustomAttributes)
 | |
|             {
 | |
|                 if (item.AttributeType.FullName == typeof(CodegenExcludeAttribute).FullName)
 | |
|                     return true;
 | |
|             }
 | |
| 
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Returns if type uses CodegenExcludeAttribute.
 | |
|         /// </summary>
 | |
|         public bool CodegenExclude(SR.MethodInfo methodInfo)
 | |
|         {
 | |
|             foreach (SR.CustomAttributeData item in methodInfo.CustomAttributes)
 | |
|             {
 | |
|                 if (item.AttributeType == typeof(CodegenExcludeAttribute))
 | |
|                     return true;
 | |
|             }
 | |
| 
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Returns if type uses CodegenExcludeAttribute.
 | |
|         /// </summary>
 | |
|         public bool CodegenExclude(MethodDefinition methodDef)
 | |
|         {
 | |
|             foreach (CustomAttribute item in methodDef.CustomAttributes)
 | |
|             {
 | |
|                 if (item.AttributeType.FullName == CodegenExcludeAttribute_FullName)
 | |
|                     return true;
 | |
|             }
 | |
| 
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Returns if type uses CodegenExcludeAttribute.
 | |
|         /// </summary>
 | |
|         public bool CodegenExclude(FieldDefinition fieldDef)
 | |
|         {
 | |
|             foreach (CustomAttribute item in fieldDef.CustomAttributes)
 | |
|             {
 | |
|                 if (item.AttributeType.FullName == CodegenExcludeAttribute_FullName)
 | |
|                     return true;
 | |
|             }
 | |
| 
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Returns if type uses CodegenIncludeAttribute.
 | |
|         /// </summary>
 | |
|         public bool CodegenInclude(FieldDefinition fieldDef)
 | |
|         {
 | |
|             foreach (CustomAttribute item in fieldDef.CustomAttributes)
 | |
|             {
 | |
|                 if (item.AttributeType.FullName == CodegenIncludeAttribute_FullName)
 | |
|                     return true;
 | |
|             }
 | |
| 
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Returns if type uses CodegenExcludeAttribute.
 | |
|         /// </summary>
 | |
|         public bool CodegenExclude(PropertyDefinition propDef)
 | |
|         {
 | |
|             foreach (CustomAttribute item in propDef.CustomAttributes)
 | |
|             {
 | |
|                 if (item.AttributeType.FullName == CodegenExcludeAttribute_FullName)
 | |
|                     return true;
 | |
|             }
 | |
| 
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Returns if type uses CodegenExcludeAttribute.
 | |
|         /// </summary>
 | |
|         public bool CodegenInclude(PropertyDefinition propDef)
 | |
|         {
 | |
|             foreach (CustomAttribute item in propDef.CustomAttributes)
 | |
|             {
 | |
|                 if (item.AttributeType.FullName == CodegenIncludeAttribute_FullName)
 | |
|                     return true;
 | |
|             }
 | |
| 
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Calls copiedMd with the assumption md shares the same parameters.
 | |
|         /// </summary>
 | |
|         public void CallCopiedMethod(MethodDefinition md, MethodDefinition copiedMd)
 | |
|         {
 | |
|             ILProcessor processor = md.Body.GetILProcessor();
 | |
|             processor.Emit(OpCodes.Ldarg_0);
 | |
|             foreach (var item in copiedMd.Parameters)
 | |
|                 processor.Emit(OpCodes.Ldarg, item);
 | |
| 
 | |
|             MethodReference mr = copiedMd.GetMethodReference(base.Session);
 | |
|             processor.Emit(OpCodes.Call, mr);
 | |
| 
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Removes countVd from list of dataFd starting at index 0.
 | |
|         /// </summary>
 | |
|         public List<Instruction> ListRemoveRange(MethodDefinition methodDef, FieldDefinition dataFd, TypeReference dataTr, VariableDefinition countVd)
 | |
|         {
 | |
|             /* Remove entries which exceed maximum buffer. */
 | |
|             //Method references for uint/data list:
 | |
|             //get_count, RemoveRange. */
 | |
|             GenericInstanceType dataListGit;
 | |
|             GetGenericLists(dataTr, out dataListGit);
 | |
|             MethodReference lstDataRemoveRangeMr = base.GetClass<GeneralHelper>().List_RemoveRange_MethodRef.MakeHostInstanceGeneric(base.Session, dataListGit);
 | |
| 
 | |
|             List<Instruction> insts = new List<Instruction>();
 | |
|             ILProcessor processor = methodDef.Body.GetILProcessor();
 | |
| 
 | |
|             //Index 1 is the uint, 0 is the data.
 | |
|             insts.Add(processor.Create(OpCodes.Ldarg_0));//this.
 | |
|             insts.Add(processor.Create(OpCodes.Ldfld, dataFd));
 | |
|             insts.Add(processor.Create(OpCodes.Ldc_I4_0));
 | |
|             insts.Add(processor.Create(OpCodes.Ldloc, countVd));
 | |
|             insts.Add(processor.Create(lstDataRemoveRangeMr.GetCallOpCode(base.Session), lstDataRemoveRangeMr));
 | |
| 
 | |
|             return insts;
 | |
|         }
 | |
|         /// <summary>
 | |
|         /// Outputs generic lists for dataTr and uint.
 | |
|         /// </summary>
 | |
|         public void GetGenericLists(TypeReference dataTr, out GenericInstanceType lstData)
 | |
|         {
 | |
|             TypeReference listDataTr = base.ImportReference(typeof(List<>));
 | |
|             lstData = listDataTr.MakeGenericInstanceType(new TypeReference[] { dataTr });
 | |
|         }
 | |
|         /// <summary>
 | |
|         /// Outputs generic lists for dataTr and uint.
 | |
|         /// </summary>
 | |
|         public void GetGenericQueues(TypeReference dataTr, out GenericInstanceType queueData)
 | |
|         {
 | |
|             TypeReference queueDataTr = base.ImportReference(typeof(Queue<>));
 | |
|             queueData = queueDataTr.MakeGenericInstanceType(new TypeReference[] { dataTr });
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Copies one method to another while transferring diagnostic paths.
 | |
|         /// </summary>
 | |
|         public MethodDefinition CopyIntoNewMethod(MethodDefinition originalMd, string toMethodName, out bool alreadyCreated)
 | |
|         {
 | |
|             TypeDefinition typeDef = originalMd.DeclaringType;
 | |
| 
 | |
|             MethodDefinition md = typeDef.GetOrCreateMethodDefinition(base.Session, toMethodName, originalMd, true, out bool created);
 | |
|             alreadyCreated = !created;
 | |
|             if (alreadyCreated)
 | |
|                 return md;
 | |
| 
 | |
|             (md.Body, originalMd.Body) = (originalMd.Body, md.Body);
 | |
|             //Move over all the debugging information
 | |
|             foreach (SequencePoint sequencePoint in originalMd.DebugInformation.SequencePoints)
 | |
|                 md.DebugInformation.SequencePoints.Add(sequencePoint);
 | |
|             originalMd.DebugInformation.SequencePoints.Clear();
 | |
| 
 | |
|             foreach (CustomDebugInformation customInfo in originalMd.CustomDebugInformations)
 | |
|                 md.CustomDebugInformations.Add(customInfo);
 | |
|             originalMd.CustomDebugInformations.Clear();
 | |
|             //Swap debuginformation scope.
 | |
|             (originalMd.DebugInformation.Scope, md.DebugInformation.Scope) = (md.DebugInformation.Scope, originalMd.DebugInformation.Scope);
 | |
| 
 | |
|             return md;
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Creates the RuntimeInitializeOnLoadMethod attribute for a method.
 | |
|         /// </summary>
 | |
|         public void CreateRuntimeInitializeOnLoadMethodAttribute(MethodDefinition methodDef, string loadType = "")
 | |
|         {
 | |
|             TypeReference attTypeRef = GetTypeReference(typeof(RuntimeInitializeOnLoadMethodAttribute));
 | |
|             foreach (CustomAttribute item in methodDef.CustomAttributes)
 | |
|             {
 | |
|                 //Already exist.
 | |
|                 if (item.AttributeType.FullName == attTypeRef.FullName)
 | |
|                     return;
 | |
|             }
 | |
| 
 | |
|             int parameterRequirement = (loadType.Length == 0) ? 0 : 1;
 | |
|             MethodDefinition constructorMethodDef = attTypeRef.GetConstructor(base.Session, parameterRequirement);
 | |
|             MethodReference constructorMethodRef = base.ImportReference(constructorMethodDef);
 | |
|             CustomAttribute ca = new CustomAttribute(constructorMethodRef);
 | |
|             /* If load type isn't null then it
 | |
|              * has to be passed in as the first argument. */
 | |
|             if (loadType.Length > 0)
 | |
|             {
 | |
|                 Type t = typeof(RuntimeInitializeLoadType);
 | |
|                 foreach (UnityEngine.RuntimeInitializeLoadType value in t.GetEnumValues())
 | |
|                 {
 | |
|                     if (loadType == value.ToString())
 | |
|                     {
 | |
|                         TypeReference tr = base.ImportReference(t);
 | |
|                         CustomAttributeArgument arg = new CustomAttributeArgument(tr, value);
 | |
|                         ca.ConstructorArguments.Add(arg);
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             methodDef.CustomAttributes.Add(ca);
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Gets the default AutoPackType to use for typeRef.
 | |
|         /// </summary>
 | |
|         /// <param name="typeRef"></param>
 | |
|         /// <returns></returns>
 | |
|         public AutoPackType GetDefaultAutoPackType(TypeReference typeRef)
 | |
|         {
 | |
|             //Singles are defauled to unpacked.
 | |
|             if (typeRef.FullName == Single_FullName)
 | |
|                 return AutoPackType.Unpacked;
 | |
|             else
 | |
|                 return AutoPackType.Packed;
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Gets the InitializeOnce method in typeDef or creates the method should it not exist.
 | |
|         /// </summary>
 | |
|         /// <param name="typeDef"></param>
 | |
|         /// <returns></returns>
 | |
|         public MethodDefinition GetOrCreateMethod(TypeDefinition typeDef, out bool created, MethodAttributes methodAttr, string methodName, TypeReference returnType)
 | |
|         {
 | |
|             MethodDefinition result = typeDef.GetMethod(methodName);
 | |
|             if (result == null)
 | |
|             {
 | |
|                 created = true;
 | |
|                 result = new MethodDefinition(methodName, methodAttr, returnType);
 | |
|                 typeDef.Methods.Add(result);
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 created = false;
 | |
|             }
 | |
| 
 | |
|             return result;
 | |
|         }
 | |
| 
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Gets a class within moduleDef or creates and returns the class if it does not already exist.
 | |
|         /// </summary>
 | |
|         /// <param name="moduleDef"></param>
 | |
|         /// <returns></returns>
 | |
|         public TypeDefinition GetOrCreateClass(out bool created, TypeAttributes typeAttr, string className, TypeReference baseTypeRef, string namespaceName = WriterProcessor.GENERATED_WRITER_NAMESPACE)
 | |
|         {
 | |
|             if (namespaceName.Length == 0)
 | |
|                 namespaceName = FishNetILPP.RUNTIME_ASSEMBLY_NAME;
 | |
| 
 | |
|             TypeDefinition type = base.Module.GetClass(className, namespaceName);
 | |
|             if (type != null)
 | |
|             {
 | |
|                 created = false;
 | |
|                 return type;
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 created = true;
 | |
|                 type = new TypeDefinition(namespaceName, className,
 | |
|                     typeAttr, base.ImportReference(typeof(object)));
 | |
|                 //Add base class if specified.
 | |
|                 if (baseTypeRef != null)
 | |
|                     type.BaseType = base.ImportReference(baseTypeRef);
 | |
| 
 | |
|                 base.Module.Types.Add(type);
 | |
|                 return type;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         #region HasNonSerializableAttribute
 | |
|         /// <summary>
 | |
|         /// Returns if fieldDef has a NonSerialized attribute.
 | |
|         /// </summary>
 | |
|         /// <param name="fieldDef"></param>
 | |
|         /// <returns></returns>
 | |
|         public bool HasNonSerializableAttribute(FieldDefinition fieldDef)
 | |
|         {
 | |
|             foreach (CustomAttribute customAttribute in fieldDef.CustomAttributes)
 | |
|             {
 | |
|                 if (customAttribute.AttributeType.FullName == NonSerialized_Attribute_FullName)
 | |
|                     return true;
 | |
|             }
 | |
| 
 | |
|             //Fall through, no matches.
 | |
|             return false;
 | |
|         }
 | |
|         /// <summary>
 | |
|         /// Returns if typeDef has a NonSerialized attribute.
 | |
|         /// </summary>
 | |
|         /// <param name="typeDef"></param>
 | |
|         /// <returns></returns>
 | |
|         public bool HasNonSerializableAttribute(TypeDefinition typeDef)
 | |
|         {
 | |
|             foreach (CustomAttribute customAttribute in typeDef.CustomAttributes)
 | |
|             {
 | |
|                 if (customAttribute.AttributeType.FullName == NonSerialized_Attribute_FullName)
 | |
|                     return true;
 | |
|             }
 | |
| 
 | |
|             //Fall through, no matches.
 | |
|             return false;
 | |
|         }
 | |
|         #endregion
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Gets a TypeReference for a type.
 | |
|         /// </summary>
 | |
|         /// <param name="type"></param>
 | |
|         public TypeReference GetTypeReference(Type type)
 | |
|         {
 | |
|             TypeReference result;
 | |
|             if (!_importedTypeReferences.TryGetValue(type, out result))
 | |
|             {
 | |
|                 result = base.ImportReference(type);
 | |
|                 _importedTypeReferences.Add(type, result);
 | |
|             }
 | |
| 
 | |
|             return result;
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Gets a FieldReference for a type.
 | |
|         /// </summary>
 | |
|         /// <param name="type"></param>
 | |
|         public FieldReference GetFieldReference(FieldDefinition fieldDef)
 | |
|         {
 | |
|             FieldReference result;
 | |
|             if (!_importedFieldReferences.TryGetValue(fieldDef, out result))
 | |
|             {
 | |
|                 result = base.ImportReference(fieldDef);
 | |
|                 _importedFieldReferences.Add(fieldDef, result);
 | |
|             }
 | |
| 
 | |
|             return result;
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Gets the current constructor for typeDef, or makes a new one if constructor doesn't exist.
 | |
|         /// </summary>
 | |
|         /// <param name="typeDef"></param>
 | |
|         /// <returns></returns>
 | |
|         public MethodDefinition GetOrCreateConstructor(TypeDefinition typeDef, out bool created, bool makeStatic)
 | |
|         {
 | |
|             // find constructor
 | |
|             MethodDefinition constructorMethodDef = typeDef.GetMethod(".cctor");
 | |
|             if (constructorMethodDef == null)
 | |
|                 constructorMethodDef = typeDef.GetMethod(".ctor");
 | |
| 
 | |
|             //Constructor already exist.
 | |
|             if (constructorMethodDef != null)
 | |
|             {
 | |
|                 if (!makeStatic)
 | |
|                     constructorMethodDef.Attributes &= ~MethodAttributes.Static;
 | |
| 
 | |
|                 created = false;
 | |
|             }
 | |
|             //Static constructor does not exist yet.
 | |
|             else
 | |
|             {
 | |
|                 created = true;
 | |
|                 MethodAttributes methodAttr = (MonoFN.Cecil.MethodAttributes.HideBySig |
 | |
|                         MonoFN.Cecil.MethodAttributes.SpecialName |
 | |
|                         MonoFN.Cecil.MethodAttributes.RTSpecialName);
 | |
|                 if (makeStatic)
 | |
|                     methodAttr |= MonoFN.Cecil.MethodAttributes.Static;
 | |
| 
 | |
|                 //Create a constructor.
 | |
|                 constructorMethodDef = new MethodDefinition(".ctor", methodAttr,
 | |
|                         typeDef.Module.TypeSystem.Void
 | |
|                         );
 | |
| 
 | |
|                 typeDef.Methods.Add(constructorMethodDef);
 | |
| 
 | |
|                 //Add ret.
 | |
|                 ILProcessor processor = constructorMethodDef.Body.GetILProcessor();
 | |
|                 processor.Emit(OpCodes.Ret);
 | |
|             }
 | |
| 
 | |
|             return constructorMethodDef;
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Creates a return of boolean type.
 | |
|         /// </summary>
 | |
|         /// <param name="processor"></param>
 | |
|         /// <param name="result"></param>
 | |
|         public void CreateRetBoolean(ILProcessor processor, bool result)
 | |
|         {
 | |
|             OpCode code = (result) ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0;
 | |
|             processor.Emit(code);
 | |
|             processor.Emit(OpCodes.Ret);
 | |
|         }
 | |
| 
 | |
|         #region Debug logging.
 | |
|         /// <summary>
 | |
|         /// Creates instructions to log using a NetworkManager or Unity logging.
 | |
|         /// </summary>
 | |
|         /// <param name="preferNetworkManager">NetworkManager will be used to log first. If the NetworkManager is unavailable Unity logging will be used.</param>
 | |
|         public List<Instruction> LogMessage(MethodDefinition md, string message, LoggingType loggingType)
 | |
|         {
 | |
|             ILProcessor processor = md.Body.GetILProcessor();
 | |
|             List<Instruction> instructions = new List<Instruction>();
 | |
|             if (loggingType == LoggingType.Off)
 | |
|             {
 | |
|                 base.LogError($"LogMessage called with LoggingType.Off.");
 | |
|                 return instructions;
 | |
|             }
 | |
| 
 | |
|             /* Try to store NetworkManager from base to a variable.
 | |
|              * If the base does not exist, such as not inheriting from NetworkBehaviour,
 | |
|              * or if null because the object is not initialized, then use InstanceFinder to
 | |
|              * retrieve the NetworkManager. Then if NetworkManager was found, perform the log. */
 | |
|             VariableDefinition networkManagerVd = CreateVariable(processor.Body.Method, typeof(NetworkManager));
 | |
| 
 | |
|             bool useStatic = (md.IsStatic || !md.DeclaringType.InheritsFrom<NetworkBehaviour>(base.Session));
 | |
|             //If does not inherit NB then use InstanceFinder.
 | |
|             if (useStatic)
 | |
|             {
 | |
|                 SetNetworkManagerFromInstanceFinder();
 | |
|             }
 | |
|             //Inherits NB, load from base.NetworkManager.
 | |
|             else
 | |
|             {
 | |
|                 instructions.Add(processor.Create(OpCodes.Ldarg_0));
 | |
|                 instructions.Add(processor.Create(OpCodes.Call, NetworkBehaviour_NetworkManager_MethodRef));
 | |
|                 instructions.Add(processor.Create(OpCodes.Stloc, networkManagerVd));
 | |
| 
 | |
|                 //If null from NB then use instancefinder.
 | |
|                 Instruction skipSetFromInstanceFinderInst = processor.Create(OpCodes.Nop);
 | |
|                 //if (nmVd == null) nmVd = InstanceFinder.NetworkManager.
 | |
|                 instructions.Add(processor.Create(OpCodes.Ldloc, networkManagerVd));
 | |
|                 instructions.Add(processor.Create(OpCodes.Brtrue_S, skipSetFromInstanceFinderInst));
 | |
|                 SetNetworkManagerFromInstanceFinder();
 | |
|                 instructions.Add(skipSetFromInstanceFinderInst);
 | |
|             }
 | |
| 
 | |
|             //Sets NetworkManager variable from instancefinder.
 | |
|             void SetNetworkManagerFromInstanceFinder()
 | |
|             {
 | |
|                 instructions.Add(processor.Create(OpCodes.Call, InstanceFinder_NetworkManager_MethodRef));
 | |
|                 instructions.Add(processor.Create(OpCodes.Stloc, networkManagerVd));
 | |
|             }
 | |
| 
 | |
|             VariableDefinition networkManagerIsNullVd = CreateVariable(md, typeof(bool));
 | |
|             //bool networkManagerIsNull = (networkManager == null);
 | |
|             instructions.Add(processor.Create(OpCodes.Ldloc, networkManagerVd));
 | |
|             instructions.Add(processor.Create(OpCodes.Ldnull));
 | |
|             instructions.Add(processor.Create(OpCodes.Ceq));
 | |
|             instructions.Add(processor.Create(OpCodes.Stloc, networkManagerIsNullVd));
 | |
| 
 | |
|             /* If (networkManagerIsNull)
 | |
|              *      networkManager.Log...
 | |
|              * else
 | |
|              *      UnityEngine.Debug.Log... */
 | |
|             Instruction afterNetworkManagerLogInst = processor.Create(OpCodes.Nop);
 | |
|             Instruction afterUnityLogInst = processor.Create(OpCodes.Nop);
 | |
|             instructions.Add(processor.Create(OpCodes.Ldloc, networkManagerIsNullVd));
 | |
|             instructions.Add(processor.Create(OpCodes.Brtrue, afterNetworkManagerLogInst));
 | |
|             instructions.AddRange(LogNetworkManagerMessage(md, networkManagerVd, message, loggingType));
 | |
|             instructions.Add(processor.Create(OpCodes.Br, afterUnityLogInst));
 | |
|             instructions.Add(afterNetworkManagerLogInst);
 | |
|             instructions.AddRange(LogUnityDebugMessage(md, message, loggingType));
 | |
|             instructions.Add(afterUnityLogInst);
 | |
| 
 | |
|             return instructions;
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Creates instructions to log using NetworkManager without error checking.
 | |
|         /// </summary>
 | |
|         public List<Instruction> LogNetworkManagerMessage(MethodDefinition md, VariableDefinition networkManagerVd, string message, LoggingType loggingType)
 | |
|         {
 | |
|             List<Instruction> instructions = new List<Instruction>();
 | |
|             if (!CanUseLogging(loggingType))
 | |
|                 return instructions;
 | |
| 
 | |
|             ILProcessor processor = md.Body.GetILProcessor();
 | |
| 
 | |
|             MethodReference methodRef;
 | |
|             if (loggingType == LoggingType.Common)
 | |
|                 methodRef = NetworkManager_LogCommon_MethodRef;
 | |
|             else if (loggingType == LoggingType.Warning)
 | |
|                 methodRef = NetworkManager_LogWarning_MethodRef;
 | |
|             else
 | |
|                 methodRef = NetworkManager_LogError_MethodRef;
 | |
| 
 | |
|             instructions.Add(processor.Create(OpCodes.Ldloc, networkManagerVd));
 | |
|             instructions.Add(processor.Create(OpCodes.Ldstr, message));
 | |
|             instructions.Add(processor.Create(OpCodes.Call, methodRef));
 | |
| 
 | |
|             return instructions;
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Creates instructions to log using Unity logging.
 | |
|         /// </summary>
 | |
|         public List<Instruction> LogUnityDebugMessage(MethodDefinition md, string message, LoggingType loggingType)
 | |
|         {
 | |
|             List<Instruction> instructions = new List<Instruction>();
 | |
|             if (!CanUseLogging(loggingType))
 | |
|                 return instructions;
 | |
| 
 | |
|             ILProcessor processor = md.Body.GetILProcessor();
 | |
| 
 | |
|             MethodReference methodRef;
 | |
|             if (loggingType == LoggingType.Common)
 | |
|                 methodRef = Debug_LogCommon_MethodRef;
 | |
|             else if (loggingType == LoggingType.Warning)
 | |
|                 methodRef = Debug_LogWarning_MethodRef;
 | |
|             else
 | |
|                 methodRef = Debug_LogError_MethodRef;
 | |
| 
 | |
|             instructions.Add(processor.Create(OpCodes.Ldstr, message));
 | |
|             instructions.Add(processor.Create(OpCodes.Call, methodRef));
 | |
|             return instructions;
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Returns if logging can be done using a LoggingType.
 | |
|         /// </summary>
 | |
|         public bool CanUseLogging(LoggingType lt)
 | |
|         {
 | |
|             if (lt == LoggingType.Off)
 | |
|             {
 | |
|                 base.LogError($"Log attempt called with LoggingType.Off.");
 | |
|                 return false;
 | |
|             }
 | |
| 
 | |
|             return true;
 | |
|         }
 | |
|         #endregion
 | |
| 
 | |
|         #region CreateVariable / CreateParameter.
 | |
|         /// <summary>
 | |
|         /// Creates a parameter within methodDef and returns it's ParameterDefinition.
 | |
|         /// </summary>
 | |
|         /// <param name="methodDef"></param>
 | |
|         /// <param name="parameterTypeRef"></param>
 | |
|         /// <returns></returns>
 | |
|         public ParameterDefinition CreateParameter(MethodDefinition methodDef, TypeDefinition parameterTypeDef, string name = "", ParameterAttributes attributes = ParameterAttributes.None, int index = -1)
 | |
|         {
 | |
|             TypeReference typeRef = methodDef.Module.ImportReference(parameterTypeDef);
 | |
|             return CreateParameter(methodDef, typeRef, name, attributes, index);
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Creates a parameter within methodDef as the next index, with the same data as passed in parameter definition.
 | |
|         /// </summary>
 | |
|         public ParameterDefinition CreateParameter(MethodDefinition methodDef, ParameterDefinition parameterTypeDef)
 | |
|         {
 | |
|             base.ImportReference(parameterTypeDef.ParameterType);
 | |
| 
 | |
|             int currentCount = methodDef.Parameters.Count;
 | |
|             string name = (parameterTypeDef.Name + currentCount);
 | |
|             ParameterDefinition parameterDef = new ParameterDefinition(name, parameterTypeDef.Attributes, parameterTypeDef.ParameterType);
 | |
|             methodDef.Parameters.Add(parameterDef);
 | |
| 
 | |
|             return parameterDef;
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Creates a parameter within methodDef and returns it's ParameterDefinition.
 | |
|         /// </summary>
 | |
|         /// <param name="methodDef"></param>
 | |
|         /// <param name="parameterTypeRef"></param>
 | |
|         /// <returns></returns>
 | |
|         public ParameterDefinition CreateParameter(MethodDefinition methodDef, TypeReference parameterTypeRef, string name = "", ParameterAttributes attributes = ParameterAttributes.None, int index = -1)
 | |
|         {
 | |
|             int currentCount = methodDef.Parameters.Count;
 | |
|             if (string.IsNullOrEmpty(name))
 | |
|                 name = (parameterTypeRef.Name + currentCount);
 | |
|             ParameterDefinition parameterDef = new ParameterDefinition(name, attributes, parameterTypeRef);
 | |
|             if (index == -1)
 | |
|                 methodDef.Parameters.Add(parameterDef);
 | |
|             else
 | |
|                 methodDef.Parameters.Insert(index, parameterDef);
 | |
|             return parameterDef;
 | |
|         }
 | |
|         /// <summary>
 | |
|         /// Creates a parameter within methodDef and returns it's ParameterDefinition.
 | |
|         /// </summary>
 | |
|         /// <param name="methodDef"></param>
 | |
|         /// <param name="parameterTypeRef"></param>
 | |
|         /// <returns></returns>
 | |
|         public ParameterDefinition CreateParameter(MethodDefinition methodDef, Type parameterType, string name = "", ParameterAttributes attributes = ParameterAttributes.None, int index = -1)
 | |
|         {
 | |
|             return CreateParameter(methodDef, GetTypeReference(parameterType), name, attributes, index);
 | |
|         }
 | |
|         /// <summary>
 | |
|         /// Creates a variable type within the body and returns it's VariableDef.
 | |
|         /// </summary>
 | |
|         /// <param name="methodDef"></param>
 | |
|         /// <param name="variableTypeRef"></param>
 | |
|         /// <returns></returns>
 | |
|         public VariableDefinition CreateVariable(MethodDefinition methodDef, TypeReference variableTypeRef)
 | |
|         {
 | |
|             VariableDefinition variableDef = new VariableDefinition(variableTypeRef);
 | |
|             methodDef.Body.Variables.Add(variableDef);
 | |
|             return variableDef;
 | |
|         }
 | |
|         /// Creates a variable type within the body and returns it's VariableDef.
 | |
|         /// </summary>
 | |
|         /// <param name="processor"></param>
 | |
|         /// <param name="methodDef"></param>
 | |
|         /// <param name="variableTypeRef"></param>
 | |
|         /// <returns></returns>
 | |
|         public VariableDefinition CreateVariable(MethodDefinition methodDef, Type variableType)
 | |
|         {
 | |
|             return CreateVariable(methodDef, GetTypeReference(variableType));
 | |
|         }
 | |
|         #endregion
 | |
| 
 | |
|         #region SetVariableDef.
 | |
|         /// <summary>
 | |
|         /// Initializes variableDef as a new object or collection of typeDef.
 | |
|         /// </summary>
 | |
|         /// <param name="processor"></param>
 | |
|         /// <param name="variableDef"></param>
 | |
|         /// <param name="typeDef"></param>
 | |
|         public void SetVariableDefinitionFromObject(ILProcessor processor, VariableDefinition variableDef, TypeDefinition typeDef)
 | |
|         {
 | |
|             TypeReference type = variableDef.VariableType;
 | |
|             if (type.IsValueType)
 | |
|             {
 | |
|                 // structs are created with Initobj
 | |
|                 processor.Emit(OpCodes.Ldloca, variableDef);
 | |
|                 processor.Emit(OpCodes.Initobj, type);
 | |
|             }
 | |
|             else if (typeDef.InheritsFrom<UnityEngine.ScriptableObject>(base.Session))
 | |
|             {
 | |
|                 MethodReference soCreateInstanceMr = processor.Body.Method.Module.ImportReference(() => UnityEngine.ScriptableObject.CreateInstance<UnityEngine.ScriptableObject>());
 | |
|                 GenericInstanceMethod genericInstanceMethod = soCreateInstanceMr.GetElementMethod().MakeGenericMethod(new TypeReference[] { type });
 | |
|                 processor.Emit(OpCodes.Call, genericInstanceMethod);
 | |
|                 processor.Emit(OpCodes.Stloc, variableDef);
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 MethodDefinition constructorMethodDef = type.GetConstructor(base.Session);
 | |
|                 if (constructorMethodDef == null)
 | |
|                 {
 | |
|                     base.LogError($"{type.Name} can't be deserialized because a default constructor could not be found. Create a default constructor or a custom serializer/deserializer.");
 | |
|                     return;
 | |
|                 }
 | |
| 
 | |
|                 MethodReference constructorMethodRef = processor.Body.Method.Module.ImportReference(constructorMethodDef);
 | |
|                 processor.Emit(OpCodes.Newobj, constructorMethodRef);
 | |
|                 processor.Emit(OpCodes.Stloc, variableDef);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Assigns value to a VariableDef.
 | |
|         /// </summary>
 | |
|         /// <param name="processor"></param>
 | |
|         /// <param name="variableDef"></param>
 | |
|         /// <param name="value"></param>
 | |
|         public void SetVariableDefinitionFromInt(ILProcessor processor, VariableDefinition variableDef, int value)
 | |
|         {
 | |
|             processor.Emit(OpCodes.Ldc_I4, value);
 | |
|             processor.Emit(OpCodes.Stloc, variableDef);
 | |
|         }
 | |
|         /// <summary>
 | |
|         /// Assigns value to a VariableDef.
 | |
|         /// </summary>
 | |
|         /// <param name="processor"></param>
 | |
|         /// <param name="variableDef"></param>
 | |
|         /// <param name="value"></param>
 | |
|         public void SetVariableDefinitionFromParameter(ILProcessor processor, VariableDefinition variableDef, ParameterDefinition value)
 | |
|         {
 | |
|             processor.Emit(OpCodes.Ldarg, value);
 | |
|             processor.Emit(OpCodes.Stloc, variableDef);
 | |
|         }
 | |
|         #endregion.
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Returns if an instruction is a call to a method.
 | |
|         /// </summary>
 | |
|         /// <param name="instruction"></param>
 | |
|         /// <param name="calledMethod"></param>
 | |
|         /// <returns></returns>
 | |
|         public bool IsCallToMethod(Instruction instruction, out MethodDefinition calledMethod)
 | |
|         {
 | |
|             if (instruction.OpCode == OpCodes.Call && instruction.Operand is MethodDefinition method)
 | |
|             {
 | |
|                 calledMethod = method;
 | |
|                 return true;
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 calledMethod = null;
 | |
|                 return false;
 | |
|             }
 | |
|         }
 | |
| 
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Returns if a serializer and deserializer exist for typeRef. 
 | |
|         /// </summary>
 | |
|         /// <param name="typeRef"></param>
 | |
|         /// <param name="create">True to create if missing.</param>
 | |
|         /// <returns></returns>
 | |
|         public bool HasSerializerAndDeserializer(TypeReference typeRef, bool create)
 | |
|         {
 | |
|             //Make sure it's imported into current module.
 | |
|             typeRef = base.ImportReference(typeRef);
 | |
|             //Can be serialized/deserialized.
 | |
|             bool hasWriter = base.GetClass<WriterProcessor>().HasSerializer(typeRef, create);
 | |
|             bool hasReader = base.GetClass<ReaderProcessor>().HasDeserializer(typeRef, create);
 | |
| 
 | |
|             return (hasWriter && hasReader);
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Creates a return of default value for methodDef.
 | |
|         /// </summary>
 | |
|         /// <returns></returns>
 | |
|         public List<Instruction> CreateRetDefault(MethodDefinition methodDef, ModuleDefinition importReturnModule = null)
 | |
|         {
 | |
|             ILProcessor processor = methodDef.Body.GetILProcessor();
 | |
|             List<Instruction> instructions = new List<Instruction>();
 | |
|             //If requires a value return.
 | |
|             if (methodDef.ReturnType != methodDef.Module.TypeSystem.Void)
 | |
|             {
 | |
|                 //Import type first.
 | |
|                 methodDef.Module.ImportReference(methodDef.ReturnType);
 | |
|                 if (importReturnModule != null)
 | |
|                     importReturnModule.ImportReference(methodDef.ReturnType);
 | |
|                 VariableDefinition vd = base.GetClass<GeneralHelper>().CreateVariable(methodDef, methodDef.ReturnType);
 | |
|                 instructions.Add(processor.Create(OpCodes.Ldloca_S, vd));
 | |
|                 instructions.Add(processor.Create(OpCodes.Initobj, vd.VariableType));
 | |
|                 instructions.Add(processor.Create(OpCodes.Ldloc, vd));
 | |
|             }
 | |
|             instructions.Add(processor.Create(OpCodes.Ret));
 | |
| 
 | |
|             return instructions;
 | |
|         }
 | |
| 
 | |
|         #region GeneratedComparers
 | |
|         /// <summary>
 | |
|         /// Creates an equality comparer for dataTr.
 | |
|         /// </summary>
 | |
|         public MethodDefinition CreateEqualityComparer(TypeReference dataTr)
 | |
|         {
 | |
|             bool created;
 | |
|             MethodDefinition comparerMd;
 | |
|             if (!_comparerDelegates.TryGetValue(dataTr.FullName, out comparerMd))
 | |
|             {
 | |
|                 comparerMd = GetOrCreateMethod(GeneratedComparer_ClassTypeDef, out created, WriterProcessor.GENERATED_METHOD_ATTRIBUTES,
 | |
|                     $"Comparer___{dataTr.FullName}", base.Module.TypeSystem.Boolean);
 | |
| 
 | |
|                 /* Nullables are not yet supported for automatic
 | |
|                 * comparers. Let user know they must make their own. */
 | |
|                 if (dataTr.IsGenericInstance)// dataTr.IsNullable(base.Session))
 | |
|                 {
 | |
|                     base.LogError($"Equality comparers cannot be automatically generated for generic types. Create a custom comparer for {dataTr.FullName}.");
 | |
|                     return null;
 | |
|                 }
 | |
|                 if (dataTr.IsArray)
 | |
|                 {
 | |
|                     base.LogError($"Equality comparers cannot be automatically generated for collections. Create a custom comparer for {dataTr.FullName}.");
 | |
|                     return null;
 | |
|                 }
 | |
| 
 | |
|                 RegisterComparerDelegate(comparerMd, dataTr);
 | |
|                 CreateComparerMethod();
 | |
|                 CreateComparerDelegate(comparerMd, dataTr);
 | |
|             }
 | |
| 
 | |
|             return comparerMd;
 | |
| 
 | |
|             void CreateComparerMethod()
 | |
|             {
 | |
|                 //Add parameters.
 | |
|                 ParameterDefinition v0Pd = CreateParameter(comparerMd, dataTr, "value0");
 | |
|                 ParameterDefinition v1Pd = CreateParameter(comparerMd, dataTr, "value1");
 | |
|                 ILProcessor processor = comparerMd.Body.GetILProcessor();
 | |
|                 comparerMd.Body.InitLocals = true;
 | |
| 
 | |
|                 /* If type is a Unity type do not try to 
 | |
|                  * create a comparer other than ref comparer, as Unity will have built in ones. */
 | |
|                 if (dataTr.CachedResolve(base.Session).Module.Name.Contains("UnityEngine"))
 | |
|                 {
 | |
|                     CreateValueOrReferenceComparer();
 | |
|                 }
 | |
|                 /* Generic types must have a comparer created for the
 | |
|                  * generic encapulation as well the argument types. */
 | |
|                 else if (dataTr.IsGenericInstance)
 | |
|                 {
 | |
|                     CreateGenericInstanceComparer();
 | |
|                     //Create a class or struct comparer for the container.
 | |
|                     if (!dataTr.IsClassOrStruct(base.Session))
 | |
|                     {
 | |
|                         base.Session.LogError($"Generic data type {dataTr} was expected to be in a container but is not.");
 | |
|                         return;
 | |
|                     }
 | |
|                     else
 | |
|                     {
 | |
|                         CreateClassOrStructComparer();
 | |
|                     }
 | |
|                 }
 | |
|                 //Class or struct.
 | |
|                 else if (dataTr.IsClassOrStruct(base.Session))
 | |
|                 {
 | |
|                     CreateClassOrStructComparer();
 | |
|                 }
 | |
|                 //Value type.
 | |
|                 else if (dataTr.IsValueType)
 | |
|                 {
 | |
|                     CreateValueOrReferenceComparer();
 | |
|                 }
 | |
|                 //Unhandled type.
 | |
|                 else
 | |
|                 {
 | |
|                     base.Session.LogError($"Comparer data type {dataTr.FullName} is unhandled.");
 | |
|                     return;
 | |
|                 }
 | |
| 
 | |
|                 void CreateGenericInstanceComparer()
 | |
|                 {
 | |
|                     /* Create for arguments first. */
 | |
|                     GenericInstanceType git = dataTr as GenericInstanceType;
 | |
|                     if (git == null || git.GenericArguments.Count == 0)
 | |
|                     {
 | |
|                         base.LogError($"Comparer data is generic but generic type returns null, or has no generic arguments.");
 | |
|                         return;
 | |
|                     }
 | |
|                     foreach (TypeReference tr in git.GenericArguments)
 | |
|                     {
 | |
|                         TypeReference trImported = base.ImportReference(tr);
 | |
|                         CreateEqualityComparer(trImported);
 | |
|                     }
 | |
|                 }
 | |
| 
 | |
| 
 | |
| 
 | |
|                 void CreateClassOrStructComparer()
 | |
|                 {
 | |
|                     //Class or struct.
 | |
|                     Instruction exitMethodInst = processor.Create(OpCodes.Ldc_I4_0);
 | |
| 
 | |
|                     //Fields.
 | |
|                     foreach (FieldDefinition fieldDef in dataTr.FindAllSerializableFields(base.Session
 | |
|                         , null, WriterProcessor.EXCLUDED_ASSEMBLY_PREFIXES))
 | |
|                     {
 | |
|                         base.ImportReference(fieldDef);
 | |
|                         MethodDefinition recursiveMd = CreateEqualityComparer(fieldDef.FieldType);
 | |
|                         if (recursiveMd == null)
 | |
|                             break;
 | |
|                         processor.Append(GetLoadParameterInstruction(comparerMd, v0Pd));
 | |
|                         processor.Emit(OpCodes.Ldfld, fieldDef);
 | |
|                         processor.Append(GetLoadParameterInstruction(comparerMd, v1Pd));
 | |
|                         processor.Emit(OpCodes.Ldfld, fieldDef);
 | |
|                         FinishTypeReferenceCompare(fieldDef.FieldType);
 | |
|                     }
 | |
| 
 | |
|                     //Properties.
 | |
|                     foreach (PropertyDefinition propertyDef in dataTr.FindAllSerializableProperties(base.Session
 | |
|                         , null, WriterProcessor.EXCLUDED_ASSEMBLY_PREFIXES))
 | |
|                     {
 | |
|                         MethodReference getMr = base.Module.ImportReference(propertyDef.GetMethod);
 | |
|                         MethodDefinition recursiveMd = CreateEqualityComparer(getMr.ReturnType);
 | |
|                         if (recursiveMd == null)
 | |
|                             break;
 | |
|                         processor.Append(GetLoadParameterInstruction(comparerMd, v0Pd));
 | |
|                         processor.Emit(OpCodes.Call, getMr);
 | |
|                         processor.Append(GetLoadParameterInstruction(comparerMd, v1Pd));
 | |
|                         processor.Emit(OpCodes.Call, getMr);
 | |
|                         FinishTypeReferenceCompare(propertyDef.PropertyType);
 | |
|                     }
 | |
| 
 | |
|                     //Return true;
 | |
|                     processor.Emit(OpCodes.Ldc_I4_1);
 | |
|                     processor.Emit(OpCodes.Ret);
 | |
|                     processor.Append(exitMethodInst);
 | |
|                     processor.Emit(OpCodes.Ret);
 | |
| 
 | |
| 
 | |
|                     void FinishTypeReferenceCompare(TypeReference tr)
 | |
|                     {
 | |
|                         /* If a class or struct see if it already has a comparer
 | |
|                          * using IEquatable. If so then call the comparer method.
 | |
|                          * Otherwise make a new comparer and call it. */
 | |
|                         if (tr.IsClassOrStruct(base.Session))
 | |
|                         {
 | |
|                             //Make equatable for type.
 | |
|                             GenericInstanceType git = IEquatable_TypeRef.MakeGenericInstanceType(tr);
 | |
|                             bool createNestedComparer = !tr.CachedResolve(base.Session).ImplementsInterface(git.FullName);
 | |
|                             //Create new.
 | |
|                             if (createNestedComparer)
 | |
|                             {
 | |
|                                 MethodDefinition cMd = CreateEqualityComparer(tr);
 | |
|                                 processor.Emit(OpCodes.Call, cMd);
 | |
|                                 processor.Emit(OpCodes.Brfalse, exitMethodInst);
 | |
|                             }
 | |
|                             //Call existing.
 | |
|                             else
 | |
|                             {
 | |
|                                 MethodDefinition cMd = tr.CachedResolve(base.Session).GetMethod("op_Equality");
 | |
|                                 if (cMd == null)
 | |
|                                 {
 | |
|                                     base.LogError($"Type {tr.FullName} implements IEquatable but the comparer method could not be found.");
 | |
|                                     return;
 | |
|                                 }
 | |
|                                 else
 | |
|                                 {
 | |
|                                     MethodReference mr = base.ImportReference(cMd);
 | |
|                                     processor.Emit(OpCodes.Call, mr);
 | |
|                                     processor.Emit(OpCodes.Brfalse, exitMethodInst);
 | |
|                                 }
 | |
|                             }
 | |
|                         }
 | |
|                         //Value types do not need to check custom comparers.
 | |
|                         else
 | |
|                         {
 | |
|                             processor.Emit(OpCodes.Bne_Un, exitMethodInst);
 | |
|                         }
 | |
| 
 | |
|                     }
 | |
|                 }
 | |
| 
 | |
|                 void CreateValueOrReferenceComparer()
 | |
|                 {
 | |
|                     base.ImportReference(dataTr);
 | |
|                     processor.Append(GetLoadParameterInstruction(comparerMd, v0Pd));
 | |
|                     processor.Append(GetLoadParameterInstruction(comparerMd, v1Pd));
 | |
|                     processor.Emit(OpCodes.Ceq);
 | |
|                     processor.Emit(OpCodes.Ret);
 | |
|                 }
 | |
| 
 | |
|             }
 | |
| 
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Registers a comparer method.
 | |
|         /// </summary>
 | |
|         /// <param name="methodDef"></param>
 | |
|         /// <param name="dataTr"></param>
 | |
|         public void RegisterComparerDelegate(MethodDefinition methodDef, TypeReference dataTr)
 | |
|         {
 | |
|             _comparerDelegates.Add(dataTr.FullName, methodDef);
 | |
|         }
 | |
|         /// <summary>
 | |
|         /// Creates a delegate for GeneratedComparers.
 | |
|         /// </summary>
 | |
|         public void CreateComparerDelegate(MethodDefinition comparerMd, TypeReference dataTr)
 | |
|         {
 | |
|             dataTr = base.ImportReference(dataTr);
 | |
|             //Initialize delegate for made comparer.
 | |
|             List<Instruction> insts = new List<Instruction>();
 | |
|             ILProcessor processor = GeneratedComparer_OnLoadMethodDef.Body.GetILProcessor();
 | |
|             //Create a Func<Reader, T> delegate 
 | |
|             insts.Add(processor.Create(OpCodes.Ldnull));
 | |
|             insts.Add(processor.Create(OpCodes.Ldftn, comparerMd));
 | |
| 
 | |
|             GenericInstanceType git;
 | |
|             git = FunctionT3TypeRef.MakeGenericInstanceType(dataTr, dataTr, GetTypeReference(typeof(bool)));
 | |
|             MethodReference functionConstructorInstanceMethodRef = FunctionT3ConstructorMethodRef.MakeHostInstanceGeneric(base.Session, git);
 | |
|             insts.Add(processor.Create(OpCodes.Newobj, functionConstructorInstanceMethodRef));
 | |
| 
 | |
|             //Call delegate to ReplicateComparer.Compare(T, T);
 | |
|             git = GeneratedComparer_TypeRef.MakeGenericInstanceType(dataTr);
 | |
|             MethodReference comparerMr = GeneratedComparer_Compare_Set_MethodRef.MakeHostInstanceGeneric(base.Session, git);
 | |
|             insts.Add(processor.Create(OpCodes.Call, comparerMr));
 | |
|             processor.InsertFirst(insts);
 | |
|         }
 | |
| 
 | |
| 
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Returns an OpCode for loading a parameter.
 | |
|         /// </summary>
 | |
|         public OpCode GetLoadParameterOpCode(ParameterDefinition pd)
 | |
|         {
 | |
|             TypeReference tr = pd.ParameterType;
 | |
|             return (tr.IsValueType && tr.IsClassOrStruct(base.Session)) ? OpCodes.Ldarga : OpCodes.Ldarg;
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Returns an instruction for loading a parameter.s
 | |
|         /// </summary>
 | |
|         public Instruction GetLoadParameterInstruction(MethodDefinition md, ParameterDefinition pd)
 | |
|         {
 | |
|             ILProcessor processor = md.Body.GetILProcessor();
 | |
|             OpCode oc = GetLoadParameterOpCode(pd);
 | |
|             return processor.Create(oc, pd);
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Creates an IsDefault comparer for dataTr.
 | |
|         /// </summary>
 | |
|         public void CreateIsDefaultComparer(TypeReference dataTr, MethodDefinition compareMethodDef)
 | |
|         {
 | |
|             GeneralHelper gh = base.GetClass<GeneralHelper>();
 | |
| 
 | |
|             MethodDefinition isDefaultMd = gh.GetOrCreateMethod(GeneratedComparer_ClassTypeDef, out bool created, WriterProcessor.GENERATED_METHOD_ATTRIBUTES,
 | |
|                 $"IsDefault___{dataTr.FullName}", base.Module.TypeSystem.Boolean);
 | |
|             //Already done. This can happen if the same replicate data is used in multiple places.
 | |
|             if (!created)
 | |
|                 return;
 | |
| 
 | |
|             MethodReference compareMr = base.ImportReference(compareMethodDef);
 | |
|             CreateIsDefaultMethod();
 | |
|             CreateIsDefaultDelegate();
 | |
| 
 | |
|             void CreateIsDefaultMethod()
 | |
|             {
 | |
|                 //Add parameters.
 | |
|                 ParameterDefinition v0Pd = gh.CreateParameter(isDefaultMd, dataTr, "value0");
 | |
|                 ILProcessor processor = isDefaultMd.Body.GetILProcessor();
 | |
|                 isDefaultMd.Body.InitLocals = true;
 | |
| 
 | |
| 
 | |
|                 processor.Emit(OpCodes.Ldarg, v0Pd);
 | |
|                 //If a struct.
 | |
|                 if (dataTr.IsValueType)
 | |
|                 {
 | |
|                     //Init a default local.
 | |
|                     VariableDefinition defaultVd = gh.CreateVariable(isDefaultMd, dataTr);
 | |
|                     processor.Emit(OpCodes.Ldloca, defaultVd);
 | |
|                     processor.Emit(OpCodes.Initobj, dataTr);
 | |
|                     processor.Emit(OpCodes.Ldloc, defaultVd);
 | |
|                 }
 | |
|                 //If a class.
 | |
|                 else
 | |
|                 {
 | |
|                     processor.Emit(OpCodes.Ldnull);
 | |
|                 }
 | |
| 
 | |
|                 processor.Emit(OpCodes.Call, compareMr);
 | |
|                 processor.Emit(OpCodes.Ret);
 | |
| 
 | |
| 
 | |
|             }
 | |
| 
 | |
|             //Creates a delegate to compare two of replicateTr.
 | |
|             void CreateIsDefaultDelegate()
 | |
|             {
 | |
|                 //Initialize delegate for made comparer.
 | |
|                 List<Instruction> insts = new List<Instruction>();
 | |
|                 ILProcessor processor = GeneratedComparer_OnLoadMethodDef.Body.GetILProcessor();
 | |
|                 //Create a Func<Reader, T> delegate 
 | |
|                 insts.Add(processor.Create(OpCodes.Ldnull));
 | |
|                 insts.Add(processor.Create(OpCodes.Ldftn, isDefaultMd));
 | |
| 
 | |
|                 GenericInstanceType git;
 | |
|                 git = gh.FunctionT2TypeRef.MakeGenericInstanceType(dataTr, gh.GetTypeReference(typeof(bool)));
 | |
|                 MethodReference funcCtorMethodRef = gh.FunctionT2ConstructorMethodRef.MakeHostInstanceGeneric(base.Session, git);
 | |
|                 insts.Add(processor.Create(OpCodes.Newobj, funcCtorMethodRef));
 | |
| 
 | |
|                 //Call delegate to ReplicateComparer.IsDefault(T).
 | |
|                 git = GeneratedComparer_TypeRef.MakeGenericInstanceType(dataTr);
 | |
|                 MethodReference isDefaultMr = GeneratedComparer_IsDefault_Set_MethodRef.MakeHostInstanceGeneric(base.Session, git);
 | |
|                 insts.Add(processor.Create(OpCodes.Call, isDefaultMr));
 | |
|                 processor.InsertFirst(insts);
 | |
|             }
 | |
| 
 | |
|         }
 | |
|         #endregion
 | |
|     }
 | |
| }
 | |
| 
 |