1158 lines
		
	
	
		
			50 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			1158 lines
		
	
	
		
			50 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| using FishNet.CodeGenerating.Extension;
 | |
| using FishNet.CodeGenerating.Helping.Extension;
 | |
| using FishNet.CodeGenerating.ILCore;
 | |
| using FishNet.Object;
 | |
| using FishNet.Serializing;
 | |
| using FishNet.Utility.Performance;
 | |
| using MonoFN.Cecil;
 | |
| using MonoFN.Cecil.Cil;
 | |
| using MonoFN.Cecil.Rocks;
 | |
| using System;
 | |
| using System.Collections.Generic;
 | |
| using SR = System.Reflection;
 | |
| using UnityDebug = UnityEngine.Debug;
 | |
| 
 | |
| namespace FishNet.CodeGenerating.Helping
 | |
| {
 | |
| 
 | |
|     internal class WriterProcessor : CodegenBase
 | |
|     {
 | |
|         #region Reflection references.
 | |
|         public readonly Dictionary<string, MethodReference> InstancedWriterMethods = new Dictionary<string, MethodReference>();
 | |
|         public readonly Dictionary<string, MethodReference> StaticWriterMethods = new Dictionary<string, MethodReference>();
 | |
|         public HashSet<TypeReference> AutoPackedMethods = new HashSet<TypeReference>(new TypeReferenceComparer());
 | |
| 
 | |
|         public TypeDefinition GeneratedWriterClassTypeDef;
 | |
|         public MethodDefinition GeneratedWriterOnLoadMethodDef;
 | |
|         #endregion
 | |
| 
 | |
|         #region Misc.
 | |
|         /// <summary>
 | |
|         /// TypeReferences which have already had delegates made for.
 | |
|         /// </summary>
 | |
|         private HashSet<TypeReference> _delegatedTypes = new HashSet<TypeReference>();
 | |
|         #endregion
 | |
| 
 | |
|         #region Const.
 | |
|         /// <summary>
 | |
|         /// Namespace to use for generated serializers and delegates.
 | |
|         /// </summary>
 | |
|         public const string GENERATED_WRITER_NAMESPACE = "FishNet.Serializing.Generated";
 | |
|         /// <summary>
 | |
|         /// Name to use for generated serializers class.
 | |
|         /// </summary>
 | |
|         public const string GENERATED_WRITERS_CLASS_NAME = "GeneratedWriters___Internal";
 | |
|         /// <summary>
 | |
|         /// Attributes to use for generated serializers class.
 | |
|         /// </summary>
 | |
|         public const TypeAttributes GENERATED_TYPE_ATTRIBUTES = (TypeAttributes.BeforeFieldInit | TypeAttributes.Class | TypeAttributes.AnsiClass |
 | |
|             TypeAttributes.Public | TypeAttributes.AutoClass | TypeAttributes.Abstract | TypeAttributes.Sealed);
 | |
|         /// <summary>
 | |
|         /// Name to use for InitializeOnce method.
 | |
|         /// </summary>
 | |
|         public const string INITIALIZEONCE_METHOD_NAME = "InitializeOnce";
 | |
|         /// <summary>
 | |
|         /// Attributes to use for InitializeOnce method within generated serializer classes.
 | |
|         /// </summary>
 | |
|         public const MethodAttributes INITIALIZEONCE_METHOD_ATTRIBUTES = (MethodAttributes.Static | MethodAttributes.Private | MethodAttributes.HideBySig);
 | |
|         /// <summary>
 | |
|         /// Attritbutes to use for generated serializers.
 | |
|         /// </summary>
 | |
|         public const MethodAttributes GENERATED_METHOD_ATTRIBUTES = (MethodAttributes.Static | MethodAttributes.Public | MethodAttributes.HideBySig);
 | |
|         /// <summary>
 | |
|         /// Prefix all built-in and user created write methods should begin with.
 | |
|         /// </summary>
 | |
|         internal const string WRITE_PREFIX = "Write";
 | |
|         /// <summary>
 | |
|         /// Prefix all built-in and user created write methods should begin with.
 | |
|         /// </summary>
 | |
|         internal const string GENERATED_WRITE_PREFIX = "Write___";
 | |
|         /// <summary>
 | |
|         /// Types to exclude from being scanned for auto serialization.
 | |
|         /// </summary>
 | |
|         public static readonly System.Type[] EXCLUDED_AUTO_SERIALIZER_TYPES = new System.Type[]
 | |
|         {
 | |
|             typeof(NetworkBehaviour)
 | |
|         };
 | |
|         /// <summary>
 | |
|         /// Types within assemblies which begin with these prefixes will not have serializers created for them.
 | |
|         /// </summary>
 | |
|         public static readonly string[] EXCLUDED_ASSEMBLY_PREFIXES = new string[]
 | |
|         {
 | |
|             "UnityEngine."
 | |
|         };
 | |
|         #endregion
 | |
| 
 | |
|         public override bool ImportReferences() => true;
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Processes data. To be used after everything else has called ImportReferences.
 | |
|         /// </summary>
 | |
|         /// <returns></returns>
 | |
|         public bool Process()
 | |
|         {
 | |
|             GeneralHelper gh = base.GetClass<GeneralHelper>();
 | |
| 
 | |
|             CreateGeneratedClassData();
 | |
|             FindInstancedWriters();
 | |
|             CreateInstancedWriterExtensions();
 | |
| 
 | |
|             //Creates class for generated writers, and init on load method.
 | |
|             void CreateGeneratedClassData()
 | |
|             {
 | |
|                 GeneratedWriterClassTypeDef = gh.GetOrCreateClass(out _, GENERATED_TYPE_ATTRIBUTES, GENERATED_WRITERS_CLASS_NAME, null, WriterProcessor.GENERATED_WRITER_NAMESPACE);
 | |
|                 /* If constructor isn't set then try to get or create it
 | |
|                  * and also add it to methods if were created. */
 | |
|                 GeneratedWriterOnLoadMethodDef = gh.GetOrCreateMethod(GeneratedWriterClassTypeDef, out _, INITIALIZEONCE_METHOD_ATTRIBUTES, INITIALIZEONCE_METHOD_NAME, base.Module.TypeSystem.Void);
 | |
|                 ILProcessor pp = GeneratedWriterOnLoadMethodDef.Body.GetILProcessor();
 | |
|                 pp.Emit(OpCodes.Ret);
 | |
|                 gh.CreateRuntimeInitializeOnLoadMethodAttribute(GeneratedWriterOnLoadMethodDef);
 | |
|             }
 | |
| 
 | |
|             //Finds all instanced writers and autopack types.
 | |
|             void FindInstancedWriters()
 | |
|             {
 | |
|                 Type pooledWriterType = typeof(PooledWriter);
 | |
|                 foreach (SR.MethodInfo methodInfo in pooledWriterType.GetMethods())
 | |
|                 {
 | |
|                     if (IsSpecialWriteMethod(methodInfo))
 | |
|                         continue;
 | |
|                     bool autoPackMethod;
 | |
|                     if (IsIgnoredWriteMethod(methodInfo, out autoPackMethod))
 | |
|                         continue;
 | |
| 
 | |
|                     MethodReference methodRef = base.ImportReference(methodInfo);
 | |
|                     /* TypeReference for the first parameter in the write method. 
 | |
|                      * The first parameter will always be the type written. */
 | |
|                     TypeReference typeRef = base.ImportReference(methodRef.Parameters[0].ParameterType);
 | |
|                     /* If here all checks pass. */
 | |
|                     AddWriterMethod(typeRef, methodRef, true, true);
 | |
|                     if (autoPackMethod)
 | |
|                         AutoPackedMethods.Add(typeRef);
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             return true;
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Returns if a MethodInfo is considered a special write method.
 | |
|         /// Special write methods have declared references within this class, and will not have extensions made for them.
 | |
|         /// </summary>
 | |
|         public bool IsSpecialWriteMethod(SR.MethodInfo methodInfo)
 | |
|         {
 | |
|             /* Special methods. */
 | |
|             if (methodInfo.Name == nameof(PooledWriter.Dispose))
 | |
|                 return true;
 | |
|             else if (methodInfo.Name == nameof(PooledWriter.WritePackedWhole))
 | |
|                 return true;
 | |
|             else if (methodInfo.Name == nameof(PooledWriter.WriteDictionary))
 | |
|                 return true;
 | |
|             else if (methodInfo.Name == nameof(PooledWriter.WriteList))
 | |
|                 return true;
 | |
| 
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Returns if a write method should be ignored.
 | |
|         /// </summary>
 | |
|         public bool IsIgnoredWriteMethod(SR.MethodInfo methodInfo, out bool autoPackMethod)
 | |
|         {
 | |
|             autoPackMethod = false;
 | |
| 
 | |
|             if (base.GetClass<GeneralHelper>().CodegenExclude(methodInfo))
 | |
|                 return true;
 | |
|             //Not long enough to be a write method.
 | |
|             else if (methodInfo.Name.Length < WRITE_PREFIX.Length)
 | |
|                 return true;
 | |
|             //Method name doesn't start with writePrefix.
 | |
|             else if (methodInfo.Name.Substring(0, WRITE_PREFIX.Length) != WRITE_PREFIX)
 | |
|                 return true;
 | |
| 
 | |
|             SR.ParameterInfo[] parameterInfos = methodInfo.GetParameters();
 | |
|             /* No parameters or more than 2 parameters. Most Write methods
 | |
|             * will have only 1 parameter but some will have 2 if
 | |
|             * there is a pack option. */
 | |
|             if (parameterInfos.Length < 1 || parameterInfos.Length > 2)
 | |
|                 return true;
 | |
|             /* If two parameters make sure the second parameter
 | |
|              * is a pack parameter. */
 | |
|             if (parameterInfos.Length == 2)
 | |
|             {
 | |
|                 autoPackMethod = (parameterInfos[1].ParameterType == typeof(AutoPackType));
 | |
|                 if (!autoPackMethod)
 | |
|                     return true;
 | |
|             }
 | |
| 
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Creates writer extension methods for built-in writers.
 | |
|         /// </summary>
 | |
|         private void CreateInstancedWriterExtensions()
 | |
|         {
 | |
|             //return;
 | |
|             if (!FishNetILPP.IsFishNetAssembly(base.Session))
 | |
|                 return;
 | |
| 
 | |
|             GeneralHelper gh = base.GetClass<GeneralHelper>();
 | |
|             WriterProcessor gwh = base.GetClass<WriterProcessor>();
 | |
| 
 | |
|             //List<MethodReference> staticReaders = new List<MethodReference>();
 | |
|             foreach (KeyValuePair<string, MethodReference> item in InstancedWriterMethods)
 | |
|             {
 | |
|                 MethodReference instancedWriteMr = item.Value;
 | |
|                 if (instancedWriteMr.HasGenericParameters)
 | |
|                     continue;
 | |
| 
 | |
|                 TypeReference valueTr = instancedWriteMr.Parameters[0].ParameterType;
 | |
| 
 | |
|                 MethodDefinition md = new MethodDefinition($"InstancedExtension___{instancedWriteMr.Name}",
 | |
|                     WriterProcessor.GENERATED_METHOD_ATTRIBUTES,
 | |
|                     base.Module.TypeSystem.Void);
 | |
| 
 | |
|                 //Add extension parameter.
 | |
|                 ParameterDefinition writerPd = gh.CreateParameter(md, typeof(Writer), "writer");
 | |
|                 //Add parameters needed by instanced writer.
 | |
|                 List<ParameterDefinition> otherPds = md.CreateParameters(base.Session, instancedWriteMr);
 | |
|                 gh.MakeExtensionMethod(md);
 | |
|                 //
 | |
|                 gwh.GeneratedWriterClassTypeDef.Methods.Add(md);
 | |
| 
 | |
|                 ILProcessor processor = md.Body.GetILProcessor();
 | |
|                 //Load writer.
 | |
|                 processor.Emit(OpCodes.Ldarg, writerPd);
 | |
|                 //Load args.
 | |
|                 foreach (ParameterDefinition pd in otherPds)
 | |
|                     processor.Emit(OpCodes.Ldarg, pd);
 | |
|                 //Call instanced.
 | |
|                 processor.Emit(instancedWriteMr.GetCallOpCode(base.Session), instancedWriteMr);
 | |
|                 processor.Emit(OpCodes.Ret);
 | |
|                 AddWriterMethod(valueTr, md, false, true);
 | |
|             }
 | |
| 
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Adds typeRef, methodDef to Instanced or Static write methods.
 | |
|         /// </summary>
 | |
|         public void AddWriterMethod(TypeReference typeRef, MethodReference methodRef, bool instanced, bool useAdd)
 | |
|         {
 | |
|             Dictionary<string, MethodReference> dict = (instanced) ?
 | |
|             InstancedWriterMethods : StaticWriterMethods;
 | |
|             string fullName = typeRef.GetFullnameWithoutBrackets();
 | |
|             if (useAdd)
 | |
|                 dict.Add(fullName, methodRef);
 | |
|             else
 | |
|                 dict[fullName] = methodRef;
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Removes typeRef from Instanced or Static write methods.
 | |
|         /// </summary>
 | |
|         internal void RemoveWriterMethod(TypeReference typeRef, bool instanced)
 | |
|         {
 | |
|             Dictionary<string, MethodReference> dict = (instanced) ?
 | |
|             InstancedWriterMethods : StaticWriterMethods;
 | |
| 
 | |
|             dict.Remove(typeRef.FullName);
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Returns if typeRef supports auto packing.
 | |
|         /// </summary>
 | |
|         public bool IsAutoPackedType(TypeReference typeRef)
 | |
|         {
 | |
|             return AutoPackedMethods.Contains(typeRef);
 | |
|         }
 | |
| 
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Creates Write<T> delegates for known static methods.
 | |
|         /// </summary>
 | |
|         public void CreateStaticMethodDelegates()
 | |
|         {
 | |
|             foreach (KeyValuePair<string, MethodReference> item in StaticWriterMethods)
 | |
|                 base.GetClass<WriterProcessor>().CreateStaticMethodWriteDelegate(item.Value);
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Creates a Write delegate for writeMethodRef and places it within the generated reader/writer constructor.
 | |
|         /// </summary>
 | |
|         /// <param name="writeMr"></param>
 | |
|         private void CreateStaticMethodWriteDelegate(MethodReference writeMr)
 | |
|         {
 | |
|             GeneralHelper gh = base.GetClass<GeneralHelper>();
 | |
|             WriterImports wi = base.GetClass<WriterImports>();
 | |
| 
 | |
|             //Check if ret already exist, if so remove it; ret will be added on again in this method.
 | |
|             if (GeneratedWriterOnLoadMethodDef.Body.Instructions.Count != 0)
 | |
|             {
 | |
|                 int lastIndex = (GeneratedWriterOnLoadMethodDef.Body.Instructions.Count - 1);
 | |
|                 if (GeneratedWriterOnLoadMethodDef.Body.Instructions[lastIndex].OpCode == OpCodes.Ret)
 | |
|                     GeneratedWriterOnLoadMethodDef.Body.Instructions.RemoveAt(lastIndex);
 | |
|             }
 | |
| 
 | |
|             ILProcessor processor = GeneratedWriterOnLoadMethodDef.Body.GetILProcessor();
 | |
|             TypeReference dataTypeRef;
 | |
|             dataTypeRef = writeMr.Parameters[1].ParameterType;
 | |
| 
 | |
|             //Check if writer already exist.
 | |
|             if (_delegatedTypes.Contains(dataTypeRef))
 | |
|             {
 | |
|                 base.LogError($"Generic write already created for {dataTypeRef.FullName}.");
 | |
|                 return;
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 _delegatedTypes.Add(dataTypeRef);
 | |
|             }
 | |
| 
 | |
|             /* Create a Action<Writer, T> delegate.
 | |
|              * May also be Action<Writer, AutoPackType, T> delegate
 | |
|              * for packed types. */
 | |
|             processor.Emit(OpCodes.Ldnull);
 | |
|             processor.Emit(OpCodes.Ldftn, writeMr);
 | |
| 
 | |
|             GenericInstanceType actionGenericInstance;
 | |
|             MethodReference actionConstructorInstanceMethodRef;
 | |
|             bool isAutoPacked = base.GetClass<WriterProcessor>().IsAutoPackedType(dataTypeRef);
 | |
| 
 | |
|             //Generate for auto pack type.
 | |
|             if (isAutoPacked)
 | |
|             {
 | |
|                 actionGenericInstance = gh.ActionT3_TypeRef.MakeGenericInstanceType(wi.WriterTypeRef, dataTypeRef, base.GetClass<WriterImports>().AutoPackTypeRef);
 | |
|                 actionConstructorInstanceMethodRef = gh.ActionT3Constructor_MethodRef.MakeHostInstanceGeneric(base.Session, actionGenericInstance);
 | |
|             }
 | |
|             //Generate for normal type.
 | |
|             else
 | |
|             {
 | |
|                 actionGenericInstance = gh.ActionT2_TypeRef.MakeGenericInstanceType(wi.WriterTypeRef, dataTypeRef);
 | |
|                 actionConstructorInstanceMethodRef = gh.ActionT2Constructor_MethodRef.MakeHostInstanceGeneric(base.Session, actionGenericInstance);
 | |
|             }
 | |
| 
 | |
|             processor.Emit(OpCodes.Newobj, actionConstructorInstanceMethodRef);
 | |
|             //Call delegate to GenericWriter<T>.Write
 | |
|             GenericInstanceType genericInstance = wi.GenericWriterTypeRef.MakeGenericInstanceType(dataTypeRef);
 | |
|             MethodReference genericrWriteMethodRef = (isAutoPacked) ?
 | |
|                 wi.WriteAutoPackGetSetMethodRef.MakeHostInstanceGeneric(base.Session, genericInstance) :
 | |
|                 wi.WriteGetSetMethodRef.MakeHostInstanceGeneric(base.Session, genericInstance);
 | |
|             processor.Emit(OpCodes.Call, genericrWriteMethodRef);
 | |
| 
 | |
|             processor.Emit(OpCodes.Ret);
 | |
|         }
 | |
| 
 | |
| 
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Returns if typeRef has a serializer.
 | |
|         /// </summary>
 | |
|         /// <param name="typeRef"></param>
 | |
|         /// <returns></returns>
 | |
|         internal bool HasSerializer(TypeReference typeRef, bool createMissing)
 | |
|         {
 | |
|             bool result = (GetInstancedWriteMethodReference(typeRef) != null) ||
 | |
|                 (GetStaticWriteMethodReference(typeRef) != null);
 | |
| 
 | |
|             if (!result && createMissing)
 | |
|             {
 | |
|                 if (!base.GetClass<GeneralHelper>().HasNonSerializableAttribute(typeRef.CachedResolve(base.Session)))
 | |
|                 {
 | |
|                     MethodReference methodRef = CreateWriter(typeRef);
 | |
|                     result = (methodRef != null);
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             return result;
 | |
|         }
 | |
| 
 | |
| 
 | |
|         #region GetWriterMethodReference.
 | |
|         /// <summary>
 | |
|         /// Returns the MethodReference for typeRef.
 | |
|         /// </summary>
 | |
|         /// <param name="typeRef"></param>
 | |
|         /// <returns></returns>
 | |
|         internal MethodReference GetInstancedWriteMethodReference(TypeReference typeRef)
 | |
|         {
 | |
|             string fullName = typeRef.GetFullnameWithoutBrackets();
 | |
|             InstancedWriterMethods.TryGetValue(fullName, out MethodReference methodRef);
 | |
|             return methodRef;
 | |
|         }
 | |
|         /// <summary>
 | |
|         /// Returns the MethodReference for typeRef.
 | |
|         /// </summary>
 | |
|         /// <param name="typeRef"></param>
 | |
|         /// <returns></returns>
 | |
|         internal MethodReference GetStaticWriteMethodReference(TypeReference typeRef)
 | |
|         {
 | |
|             string fullName = typeRef.GetFullnameWithoutBrackets();
 | |
|             StaticWriterMethods.TryGetValue(fullName, out MethodReference methodRef);
 | |
|             return methodRef;
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Returns the MethodReference for typeRef favoring instanced or static.
 | |
|         /// </summary>
 | |
|         /// <param name="typeRef"></param>
 | |
|         /// <param name="favorInstanced"></param>
 | |
|         /// <returns></returns>
 | |
|         internal MethodReference GetWriteMethodReference(TypeReference typeRef)
 | |
|         {
 | |
|             bool favorInstanced = false;
 | |
| 
 | |
|             MethodReference result;
 | |
|             if (favorInstanced)
 | |
|             {
 | |
|                 result = GetInstancedWriteMethodReference(typeRef);
 | |
|                 if (result == null)
 | |
|                     result = GetStaticWriteMethodReference(typeRef);
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 result = GetStaticWriteMethodReference(typeRef);
 | |
|                 if (result == null)
 | |
|                     result = GetInstancedWriteMethodReference(typeRef);
 | |
|             }
 | |
| 
 | |
|             return result;
 | |
|         }
 | |
|         /// <summary>
 | |
|         /// Gets the write MethodRef for typeRef, or tries to create it if not present.
 | |
|         /// </summary>
 | |
|         /// <param name="typeRef"></param>
 | |
|         /// <returns></returns>
 | |
|         internal MethodReference GetOrCreateWriteMethodReference(TypeReference typeRef)
 | |
|         {
 | |
| #pragma warning disable CS0219
 | |
|             bool favorInstanced = false;
 | |
| #pragma warning restore CS0219
 | |
|             //Try to get existing writer, if not present make one.
 | |
|             MethodReference writeMethodRef = GetWriteMethodReference(typeRef);
 | |
|             if (writeMethodRef == null)
 | |
|                 writeMethodRef = CreateWriter(typeRef);
 | |
| 
 | |
|             //If still null then return could not be generated.
 | |
|             if (writeMethodRef == null)
 | |
|             {
 | |
|                 base.LogError($"Could not create serializer for {typeRef.FullName}.");
 | |
|             }
 | |
|             //Otherwise, check if generic and create writes for generic pararameters.
 | |
|             else if (typeRef.IsGenericInstance)
 | |
|             {
 | |
|                 GenericInstanceType git = (GenericInstanceType)typeRef;
 | |
|                 foreach (TypeReference item in git.GenericArguments)
 | |
|                 {
 | |
|                     MethodReference result = GetOrCreateWriteMethodReference(item);
 | |
|                     if (result == null)
 | |
|                     {
 | |
|                         base.LogError($"Could not create serializer for {item.FullName}.");
 | |
|                         return null;
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             return writeMethodRef;
 | |
|         }
 | |
|         #endregion
 | |
| 
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Creates a PooledWriter within the body/ and returns its variable index.
 | |
|         /// EG: PooledWriter writer = WriterPool.GetWriter();
 | |
|         /// </summary>
 | |
|         internal VariableDefinition CreatePooledWriter(MethodDefinition methodDef, int length)
 | |
|         {
 | |
|             VariableDefinition resultVd;
 | |
|             List<Instruction> insts = CreatePooledWriter(methodDef, length, out resultVd);
 | |
| 
 | |
|             ILProcessor processor = methodDef.Body.GetILProcessor();
 | |
|             processor.Add(insts);
 | |
|             return resultVd;
 | |
|         }
 | |
|         /// <summary>
 | |
|         /// Creates a PooledWriter within the body/ and returns its variable index.
 | |
|         /// EG: PooledWriter writer = WriterPool.GetWriter();
 | |
|         /// </summary>
 | |
|         /// <param name="processor"></param>
 | |
|         /// <param name="methodDef"></param>
 | |
|         /// <returns></returns>
 | |
|         internal List<Instruction> CreatePooledWriter(MethodDefinition methodDef, int length, out VariableDefinition resultVd)
 | |
|         {
 | |
|             WriterImports wi = base.GetClass<WriterImports>();
 | |
| 
 | |
|             List<Instruction> insts = new List<Instruction>();
 | |
|             ILProcessor processor = methodDef.Body.GetILProcessor();
 | |
| 
 | |
|             resultVd = base.GetClass<GeneralHelper>().CreateVariable(methodDef, wi.PooledWriter_TypeRef);
 | |
|             //If length is specified then pass in length.
 | |
|             if (length > 0)
 | |
|             {
 | |
|                 insts.Add(processor.Create(OpCodes.Ldc_I4, length));
 | |
|                 insts.Add(processor.Create(OpCodes.Call, wi.WriterPool_GetWriterLength_MethodRef));
 | |
|             }
 | |
|             //Use parameter-less method if no length.
 | |
|             else
 | |
|             {
 | |
|                 insts.Add(processor.Create(OpCodes.Call, wi.WriterPool_GetWriter_MethodRef));
 | |
|             }
 | |
|             //Set value to variable definition.
 | |
|             insts.Add(processor.Create(OpCodes.Stloc, resultVd));
 | |
|             return insts;
 | |
|         }
 | |
| 
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Calls Dispose on a PooledWriter.
 | |
|         /// EG: writer.Dispose();
 | |
|         /// </summary>
 | |
|         /// <param name="processor"></param>
 | |
|         /// <param name="writerDefinition"></param>
 | |
|         internal List<Instruction> DisposePooledWriter(MethodDefinition methodDef, VariableDefinition writerDefinition)
 | |
|         {
 | |
|             WriterImports wi = base.GetClass<WriterImports>();
 | |
| 
 | |
|             List<Instruction> insts = new List<Instruction>();
 | |
|             ILProcessor processor = methodDef.Body.GetILProcessor();
 | |
| 
 | |
|             insts.Add(processor.Create(OpCodes.Ldloc, writerDefinition));
 | |
|             insts.Add(processor.Create(wi.PooledWriter_Dispose_MethodRef.GetCallOpCode(base.Session), wi.PooledWriter_Dispose_MethodRef));
 | |
| 
 | |
|             return insts;
 | |
|         }
 | |
| 
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Creates a null check on the second argument using a boolean.
 | |
|         /// </summary>
 | |
|         internal void CreateRetOnNull(ILProcessor processor, ParameterDefinition writerParameterDef, ParameterDefinition checkedParameterDef, bool useBool)
 | |
|         {
 | |
|             Instruction endIf = processor.Create(OpCodes.Nop);
 | |
|             //If (value) jmp to endIf.
 | |
|             processor.Emit(OpCodes.Ldarg, checkedParameterDef);
 | |
|             processor.Emit(OpCodes.Brtrue, endIf);
 | |
|             //writer.WriteBool / writer.WritePackedWhole
 | |
|             if (useBool)
 | |
|                 CreateWriteBool(processor, writerParameterDef, true);
 | |
|             else
 | |
|                 CreateWritePackedWhole(processor, writerParameterDef, -1);
 | |
|             //Exit method.
 | |
|             processor.Emit(OpCodes.Ret);
 | |
|             //End of if check.
 | |
|             processor.Append(endIf);
 | |
|         }
 | |
| 
 | |
|         #region CreateWritePackWhole
 | |
|         /// <summary>
 | |
|         /// Creates a call to WritePackWhole with value.
 | |
|         /// </summary>
 | |
|         /// <param name="processor"></param>
 | |
|         /// <param name="value"></param>
 | |
|         internal void CreateWritePackedWhole(ILProcessor processor, ParameterDefinition writerParameterDef, int value)
 | |
|         {
 | |
|             WriterImports wi = base.GetClass<WriterImports>();
 | |
| 
 | |
|             //Create local int and set it to value.
 | |
|             VariableDefinition intVariableDef = base.GetClass<GeneralHelper>().CreateVariable(processor.Body.Method, typeof(int));
 | |
|             base.GetClass<GeneralHelper>().SetVariableDefinitionFromInt(processor, intVariableDef, value);
 | |
|             //Writer.
 | |
|             processor.Emit(OpCodes.Ldarg, writerParameterDef);
 | |
|             //Writer.WritePackedWhole(value).
 | |
|             processor.Emit(OpCodes.Ldloc, intVariableDef);
 | |
|             processor.Emit(OpCodes.Conv_U8);
 | |
|             processor.Emit(wi.Writer_WritePackedWhole_MethodRef.GetCallOpCode(base.Session), wi.Writer_WritePackedWhole_MethodRef);
 | |
|         }
 | |
|         /// <summary>
 | |
|         /// Creates a call to WritePackWhole with value.
 | |
|         /// </summary>
 | |
|         /// <param name="processor"></param>
 | |
|         /// <param name="value"></param>
 | |
|         internal void CreateWritePackedWhole(ILProcessor processor, ParameterDefinition writerParameterDef, VariableDefinition value)
 | |
|         {
 | |
|             WriterImports wi = base.GetClass<WriterImports>();
 | |
| 
 | |
|             //Writer.
 | |
|             processor.Emit(OpCodes.Ldarg, writerParameterDef);
 | |
|             //Writer.WritePackedWhole(value).
 | |
|             processor.Emit(OpCodes.Ldloc, value);
 | |
|             processor.Emit(OpCodes.Conv_U8);
 | |
|             processor.Emit(wi.Writer_WritePackedWhole_MethodRef.GetCallOpCode(base.Session), wi.Writer_WritePackedWhole_MethodRef);
 | |
|         }
 | |
|         #endregion
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Creates a call to WriteBoolean with value.
 | |
|         /// </summary>
 | |
|         /// <param name="processor"></param>
 | |
|         /// <param name="writerParameterDef"></param>
 | |
|         /// <param name="value"></param>
 | |
|         internal void CreateWriteBool(ILProcessor processor, ParameterDefinition writerParameterDef, bool value)
 | |
|         {
 | |
|             MethodReference writeBoolMethodRef = GetWriteMethodReference(base.GetClass<GeneralHelper>().GetTypeReference(typeof(bool)));
 | |
|             processor.Emit(OpCodes.Ldarg, writerParameterDef);
 | |
|             int intValue = (value) ? 1 : 0;
 | |
|             processor.Emit(OpCodes.Ldc_I4, intValue);
 | |
|             processor.Emit(writeBoolMethodRef.GetCallOpCode(base.Session), writeBoolMethodRef);
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Creates a Write call on a PooledWriter variable for parameterDef.
 | |
|         /// EG: writer.WriteBool(xxxxx);
 | |
|         /// </summary>
 | |
|         internal List<Instruction> CreateWriteInstructions(MethodDefinition methodDef, object pooledWriterDef, ParameterDefinition valueParameterDef, MethodReference writeMr)
 | |
|         {
 | |
|             List<Instruction> insts = new List<Instruction>();
 | |
|             ILProcessor processor = methodDef.Body.GetILProcessor();
 | |
| 
 | |
|             if (writeMr != null)
 | |
|             {
 | |
|                 if (pooledWriterDef is VariableDefinition)
 | |
|                 {
 | |
|                     insts.Add(processor.Create(OpCodes.Ldloc, (VariableDefinition)pooledWriterDef));
 | |
|                 }
 | |
|                 else if (pooledWriterDef is ParameterDefinition)
 | |
|                 {
 | |
|                     insts.Add(processor.Create(OpCodes.Ldarg, (ParameterDefinition)pooledWriterDef));
 | |
|                 }
 | |
|                 else
 | |
|                 {
 | |
|                     base.LogError($"{pooledWriterDef.GetType().FullName} is not a valid writerDef. Type must be VariableDefinition or ParameterDefinition.");
 | |
|                     return new List<Instruction>();
 | |
|                 }
 | |
|                 insts.Add(processor.Create(OpCodes.Ldarg, valueParameterDef));
 | |
|                 //If an auto pack method then insert default value.
 | |
|                 if (AutoPackedMethods.Contains(valueParameterDef.ParameterType))
 | |
|                 {
 | |
|                     AutoPackType packType = base.GetClass<GeneralHelper>().GetDefaultAutoPackType(valueParameterDef.ParameterType);
 | |
|                     insts.Add(processor.Create(OpCodes.Ldc_I4, (int)packType));
 | |
|                 }
 | |
| 
 | |
|                 TypeReference valueTr = valueParameterDef.ParameterType;
 | |
|                 /* If generic then find write class for
 | |
|                  * data type. Currently we only support one generic
 | |
|                  * for this. */
 | |
|                 if (valueTr.IsGenericInstance)
 | |
|                 {
 | |
|                     GenericInstanceType git = (GenericInstanceType)valueTr;
 | |
|                     TypeReference genericTr = git.GenericArguments[0];
 | |
|                     writeMr = writeMr.GetMethodReference(base.Session, genericTr);
 | |
|                 }
 | |
| 
 | |
|                 insts.Add(processor.Create(OpCodes.Call, writeMr));
 | |
|                 return insts;
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 base.LogError($"Writer not found for {valueParameterDef.ParameterType.FullName}.");
 | |
|                 return new List<Instruction>();
 | |
|             }
 | |
|         }
 | |
|         /// <summary>
 | |
|         /// Creates a Write call on a PooledWriter variable for parameterDef.
 | |
|         /// EG: writer.WriteBool(xxxxx);
 | |
|         /// </summary>
 | |
|         internal void CreateWrite(MethodDefinition methodDef, object writerDef, ParameterDefinition valuePd, MethodReference writeMr)
 | |
|         {
 | |
|             List<Instruction> insts = CreateWriteInstructions(methodDef, writerDef, valuePd, writeMr);
 | |
|             ILProcessor processor = methodDef.Body.GetILProcessor();
 | |
|             processor.Add(insts);
 | |
|         }
 | |
|         /// <summary>
 | |
|         /// Creates a Write call to a writer.
 | |
|         /// EG: StaticClass.WriteBool(xxxxx);
 | |
|         /// </summary>
 | |
|         /// <param name="processor"></param>
 | |
|         /// <param name="fieldDef"></param>
 | |
|         internal void CreateWrite(MethodDefinition writerMd, ParameterDefinition valuePd, FieldDefinition fieldDef, MethodReference writeMr)
 | |
|         {
 | |
|             if (writeMr != null)
 | |
|             {
 | |
|                 ILProcessor processor = writerMd.Body.GetILProcessor();
 | |
|                 ParameterDefinition writerPd = writerMd.Parameters[0];
 | |
| 
 | |
|                 /* If generic then find write class for
 | |
|                  * data type. Currently we only support one generic
 | |
|                  * for this. */
 | |
|                 if (fieldDef.FieldType.IsGenericInstance)
 | |
|                 {
 | |
|                     GenericInstanceType git = (GenericInstanceType)fieldDef.FieldType;
 | |
|                     TypeReference genericTr = git.GenericArguments[0];
 | |
|                     writeMr = writeMr.GetMethodReference(base.Session, genericTr);
 | |
|                 }
 | |
| 
 | |
|                 FieldReference fieldRef = base.GetClass<GeneralHelper>().GetFieldReference(fieldDef);
 | |
|                 processor.Emit(OpCodes.Ldarg, writerPd);
 | |
|                 processor.Emit(OpCodes.Ldarg, valuePd);
 | |
|                 processor.Emit(OpCodes.Ldfld, fieldRef);
 | |
|                 //If an auto pack method then insert default value.
 | |
|                 if (AutoPackedMethods.Contains(fieldDef.FieldType))
 | |
|                 {
 | |
|                     AutoPackType packType = base.GetClass<GeneralHelper>().GetDefaultAutoPackType(fieldDef.FieldType);
 | |
|                     processor.Emit(OpCodes.Ldc_I4, (int)packType);
 | |
|                 }
 | |
|                 processor.Emit(OpCodes.Call, writeMr);
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 base.LogError($"Writer not found for {fieldDef.FieldType.FullName}.");
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Creates a Write call to a writer.
 | |
|         /// EG: StaticClass.WriteBool(xxxxx);
 | |
|         /// </summary>
 | |
|         /// <param name="processor"></param>
 | |
|         /// <param name="propertyDef"></param>
 | |
|         internal void CreateWrite(MethodDefinition writerMd, ParameterDefinition valuePd, MethodReference getMr, MethodReference writeMr)
 | |
|         {
 | |
|             TypeReference returnTr = base.ImportReference(getMr.ReturnType);
 | |
| 
 | |
|             if (writeMr != null)
 | |
|             {
 | |
|                 ILProcessor processor = writerMd.Body.GetILProcessor();
 | |
|                 ParameterDefinition writerPd = writerMd.Parameters[0];
 | |
| 
 | |
|                 /* If generic then find write class for
 | |
|                 * data type. Currently we only support one generic
 | |
|                 * for this. */
 | |
|                 if (returnTr.IsGenericInstance)
 | |
|                 {
 | |
|                     GenericInstanceType git = (GenericInstanceType)returnTr;
 | |
|                     TypeReference genericTr = git.GenericArguments[0];
 | |
|                     writeMr = writeMr.GetMethodReference(base.Session, genericTr);
 | |
|                 }
 | |
| 
 | |
|                 processor.Emit(OpCodes.Ldarg, writerPd);
 | |
|                 OpCode ldArgOC0 = (valuePd.ParameterType.IsValueType) ? OpCodes.Ldarga : OpCodes.Ldarg;
 | |
|                 processor.Emit(ldArgOC0, valuePd);
 | |
|                 processor.Emit(OpCodes.Call, getMr);
 | |
|                 //If an auto pack method then insert default value.
 | |
|                 if (AutoPackedMethods.Contains(returnTr))
 | |
|                 {
 | |
|                     AutoPackType packType = base.GetClass<GeneralHelper>().GetDefaultAutoPackType(returnTr);
 | |
|                     processor.Emit(OpCodes.Ldc_I4, (int)packType);
 | |
|                 }
 | |
|                 processor.Emit(OpCodes.Call, writeMr);
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 base.LogError($"Writer not found for {returnTr.FullName}.");
 | |
|             }
 | |
|         }
 | |
| 
 | |
| 
 | |
| 
 | |
|         #region TypeReference writer generators.
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Generates a writer for objectTypeReference if one does not already exist.
 | |
|         /// </summary>
 | |
|         /// <param name="objectTr"></param>
 | |
|         /// <returns></returns>
 | |
|         internal MethodReference CreateWriter(TypeReference objectTr)
 | |
|         {
 | |
|             MethodReference methodRefResult = null;
 | |
|             TypeDefinition objectTd;
 | |
|             SerializerType serializerType = base.GetClass<GeneratorHelper>().GetSerializerType(objectTr, true, out objectTd);
 | |
|             if (serializerType != SerializerType.Invalid)
 | |
|             {
 | |
|                 //Array.
 | |
|                 if (serializerType == SerializerType.Array)
 | |
|                     methodRefResult = CreateArrayWriterMethodReference(objectTr);
 | |
|                 //Enum.
 | |
|                 else if (serializerType == SerializerType.Enum)
 | |
|                     methodRefResult = CreateEnumWriterMethodDefinition(objectTr);
 | |
|                 //Dictionary, List, ListCache
 | |
|                 else if (serializerType == SerializerType.Dictionary
 | |
|                     || serializerType == SerializerType.List
 | |
|                     || serializerType == SerializerType.ListCache)
 | |
|                     methodRefResult = CreateGenericCollectionWriterMethodReference(objectTr, serializerType);
 | |
|                 //NetworkBehaviour.
 | |
|                 else if (serializerType == SerializerType.NetworkBehaviour)
 | |
|                     methodRefResult = CreateNetworkBehaviourWriterMethodReference(objectTd);
 | |
|                 //Nullable type.
 | |
|                 else if (serializerType == SerializerType.Nullable)
 | |
|                     methodRefResult = CreateNullableWriterMethodReference(objectTr, objectTd);
 | |
|                 //Class or struct.
 | |
|                 else if (serializerType == SerializerType.ClassOrStruct)
 | |
|                     methodRefResult = CreateClassOrStructWriterMethodDefinition(objectTr);
 | |
|             }
 | |
| 
 | |
|             //If was not created.
 | |
|             if (methodRefResult == null)
 | |
|                 RemoveFromStaticWriters(objectTr);
 | |
| 
 | |
|             return methodRefResult;
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Removes from static writers.
 | |
|         /// </summary>
 | |
|         private void RemoveFromStaticWriters(TypeReference tr)
 | |
|         {
 | |
|             base.GetClass<WriterProcessor>().RemoveWriterMethod(tr, false);
 | |
|         }
 | |
|         /// <summary>
 | |
|         /// Adds to static writers.
 | |
|         /// </summary>
 | |
|         private void AddToStaticWriters(TypeReference tr, MethodReference mr)
 | |
|         {
 | |
|             base.GetClass<WriterProcessor>().AddWriterMethod(tr, mr.CachedResolve(base.Session), false, true);
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Adds a write for a NetworkBehaviour class type to WriterMethods.
 | |
|         /// </summary>
 | |
|         /// <param name="classTypeRef"></param>
 | |
|         private MethodReference CreateNetworkBehaviourWriterMethodReference(TypeReference objectTr)
 | |
|         {
 | |
|             ObjectHelper oh = base.GetClass<ObjectHelper>();
 | |
| 
 | |
|             objectTr = base.ImportReference(objectTr.Resolve());
 | |
|             //All NetworkBehaviour types will simply WriteNetworkBehaviour/ReadNetworkBehaviour.
 | |
|             //Create generated reader/writer class. This class holds all generated reader/writers.
 | |
|             base.GetClass<GeneralHelper>().GetOrCreateClass(out _, GENERATED_TYPE_ATTRIBUTES, GENERATED_WRITERS_CLASS_NAME, null);
 | |
| 
 | |
|             MethodDefinition createdWriterMd = CreateStaticWriterStubMethodDefinition(objectTr);
 | |
|             AddToStaticWriters(objectTr, createdWriterMd);
 | |
| 
 | |
|             ILProcessor processor = createdWriterMd.Body.GetILProcessor();
 | |
| 
 | |
|             MethodReference writeMethodRef = base.GetClass<WriterProcessor>().GetOrCreateWriteMethodReference(oh.NetworkBehaviour_TypeRef);
 | |
|             //Get parameters for method.
 | |
|             ParameterDefinition writerParameterDef = createdWriterMd.Parameters[0];
 | |
|             ParameterDefinition classParameterDef = createdWriterMd.Parameters[1];
 | |
| 
 | |
|             //Load parameters as arguments.
 | |
|             processor.Emit(OpCodes.Ldarg, writerParameterDef);
 | |
|             processor.Emit(OpCodes.Ldarg, classParameterDef);
 | |
|             //writer.WriteNetworkBehaviour(arg1);
 | |
|             processor.Emit(OpCodes.Call, writeMethodRef);
 | |
| 
 | |
|             processor.Emit(OpCodes.Ret);
 | |
| 
 | |
|             return base.ImportReference(createdWriterMd);
 | |
|         }
 | |
| 
 | |
|         /// <summary> 
 | |
|         /// Gets the length of a collection and writes the value to a variable.
 | |
|         /// </summary>
 | |
|         private void CreateCollectionLength(ILProcessor processor, ParameterDefinition collectionParameterDef, VariableDefinition storeVariableDef)
 | |
|         {
 | |
|             processor.Emit(OpCodes.Ldarg, collectionParameterDef);
 | |
|             processor.Emit(OpCodes.Ldlen);
 | |
|             processor.Emit(OpCodes.Conv_I4);
 | |
|             processor.Emit(OpCodes.Stloc, storeVariableDef);
 | |
|         }
 | |
| 
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Creates a writer for a class or struct of objectTypeRef.
 | |
|         /// </summary>
 | |
|         /// <param name="objectTr"></param>
 | |
|         /// <returns></returns>
 | |
|         private MethodReference CreateNullableWriterMethodReference(TypeReference objectTr, TypeDefinition objectTd)
 | |
|         {
 | |
|             WriterProcessor wh = base.GetClass<WriterProcessor>();
 | |
| 
 | |
|             GenericInstanceType objectGit = objectTr as GenericInstanceType;
 | |
|             TypeReference valueTr = objectGit.GenericArguments[0];
 | |
| 
 | |
|             //Get the writer for the value.
 | |
|             MethodReference valueWriterMr = wh.GetOrCreateWriteMethodReference(valueTr);
 | |
|             if (valueWriterMr == null)
 | |
|                 return null;
 | |
| 
 | |
| 
 | |
|             MethodDefinition tmpMd;
 | |
|             tmpMd = objectTd.GetMethod("get_Value");
 | |
|             MethodReference genericGetValueMr = tmpMd.MakeHostInstanceGeneric(base.Session, objectGit);
 | |
|             tmpMd = objectTd.GetMethod("get_HasValue");
 | |
|             MethodReference genericHasValueMr = tmpMd.MakeHostInstanceGeneric(base.Session, objectGit);
 | |
| 
 | |
|             /* Stubs generate Method(Writer writer, T value). */
 | |
|             MethodDefinition createdWriterMd = CreateStaticWriterStubMethodDefinition(objectTr);
 | |
|             AddToStaticWriters(objectTr, createdWriterMd);
 | |
| 
 | |
|             ILProcessor processor = createdWriterMd.Body.GetILProcessor();
 | |
| 
 | |
|             //Value parameter.
 | |
|             ParameterDefinition valuePd = createdWriterMd.Parameters[1];
 | |
|             ParameterDefinition writerPd = createdWriterMd.Parameters[0];
 | |
| 
 | |
|             //Have to write a new ret on null because nullables use hasValue for null checks.
 | |
|             Instruction afterNullRetInst = processor.Create(OpCodes.Nop);
 | |
|             processor.Emit(OpCodes.Ldarga, valuePd);
 | |
|             processor.Emit(OpCodes.Call, genericHasValueMr);
 | |
|             processor.Emit(OpCodes.Brtrue_S, afterNullRetInst);
 | |
|             wh.CreateWriteBool(processor, writerPd, true);
 | |
|             processor.Emit(OpCodes.Ret);
 | |
|             processor.Append(afterNullRetInst);
 | |
| 
 | |
|             //Code will only execute here and below if not null.
 | |
|             wh.CreateWriteBool(processor, writerPd, false);
 | |
| 
 | |
|             processor.Emit(OpCodes.Ldarg, writerPd);
 | |
|             processor.Emit(OpCodes.Ldarga, valuePd);
 | |
|             processor.Emit(OpCodes.Call, genericGetValueMr);
 | |
|             //If an auto pack method then insert default value.
 | |
|             if (wh.IsAutoPackedType(valueTr))
 | |
|             {
 | |
|                 AutoPackType packType = base.GetClass<GeneralHelper>().GetDefaultAutoPackType(valueTr);
 | |
|                 processor.Emit(OpCodes.Ldc_I4, (int)packType);
 | |
|             }
 | |
|             processor.Emit(OpCodes.Call, valueWriterMr);
 | |
| 
 | |
|             processor.Emit(OpCodes.Ret);
 | |
|             return base.ImportReference(createdWriterMd);
 | |
|         }
 | |
| 
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Creates a writer for a class or struct of objectTypeRef.
 | |
|         /// </summary>
 | |
|         /// <param name="objectTr"></param>
 | |
|         /// <returns></returns>
 | |
|         private MethodReference CreateClassOrStructWriterMethodDefinition(TypeReference objectTr)
 | |
|         {
 | |
|             WriterProcessor wh = base.GetClass<WriterProcessor>();
 | |
| 
 | |
|             /*Stubs generate Method(Writer writer, T value). */
 | |
|             MethodDefinition createdWriterMd = CreateStaticWriterStubMethodDefinition(objectTr);
 | |
|             AddToStaticWriters(objectTr, createdWriterMd);
 | |
|             ILProcessor processor = createdWriterMd.Body.GetILProcessor();
 | |
| 
 | |
|             //If not a value type then add a null check.
 | |
|             if (!objectTr.CachedResolve(base.Session).IsValueType)
 | |
|             {
 | |
|                 ParameterDefinition writerPd = createdWriterMd.Parameters[0];
 | |
|                 wh.CreateRetOnNull(processor, writerPd, createdWriterMd.Parameters[1], true);
 | |
|                 //Code will only execute here and below if not null.
 | |
|                 wh.CreateWriteBool(processor, writerPd, false);
 | |
|             }
 | |
| 
 | |
|             //Write all fields for the class or struct.
 | |
|             ParameterDefinition valueParameterDef = createdWriterMd.Parameters[1];
 | |
|             if (!WriteFieldsAndProperties(createdWriterMd, valueParameterDef, objectTr))
 | |
|                 return null;
 | |
| 
 | |
|             processor.Emit(OpCodes.Ret);
 | |
|             return base.ImportReference(createdWriterMd);
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Find all fields in type and write them
 | |
|         /// </summary>
 | |
|         /// <param name="objectTr"></param>
 | |
|         /// <param name="processor"></param>
 | |
|         /// <returns>false if fail</returns>
 | |
|         private bool WriteFieldsAndProperties(MethodDefinition generatedWriteMd, ParameterDefinition valuePd, TypeReference objectTr)
 | |
|         {
 | |
|             WriterProcessor wh = base.GetClass<WriterProcessor>();
 | |
| 
 | |
|             //This probably isn't needed but I'm too afraid to remove it.
 | |
|             if (objectTr.Module != base.Module)
 | |
|                 objectTr = base.ImportReference(objectTr.CachedResolve(base.Session));
 | |
| 
 | |
|             //Fields
 | |
|             foreach (FieldDefinition fieldDef in objectTr.FindAllSerializableFields(base.Session))//, WriterHelper.EXCLUDED_AUTO_SERIALIZER_TYPES))
 | |
|             {
 | |
|                 TypeReference tr;
 | |
|                 if (fieldDef.FieldType.IsGenericInstance)
 | |
|                 {
 | |
|                     GenericInstanceType genericTr = (GenericInstanceType)fieldDef.FieldType;
 | |
|                     tr = genericTr.GenericArguments[0];
 | |
|                 }
 | |
|                 else
 | |
|                 {
 | |
|                     tr = fieldDef.FieldType;
 | |
|                 }
 | |
|                 if (GetWriteMethod(fieldDef.FieldType, out MethodReference writeMr))
 | |
|                     wh.CreateWrite(generatedWriteMd, valuePd, fieldDef, writeMr);
 | |
|             }
 | |
| 
 | |
|             //Properties.
 | |
|             foreach (PropertyDefinition propertyDef in objectTr.FindAllSerializableProperties(base.Session
 | |
|                 , WriterProcessor.EXCLUDED_AUTO_SERIALIZER_TYPES, WriterProcessor.EXCLUDED_ASSEMBLY_PREFIXES))
 | |
|             {
 | |
|                 if (GetWriteMethod(propertyDef.PropertyType, out MethodReference writerMr))
 | |
|                 {
 | |
|                     MethodReference getMr = base.Module.ImportReference(propertyDef.GetMethod);
 | |
|                     wh.CreateWrite(generatedWriteMd, valuePd, getMr, writerMr);
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             //Gets or creates writer method and outputs it. Returns true if method is found or created.
 | |
|             bool GetWriteMethod(TypeReference tr, out MethodReference writeMr)
 | |
|             {
 | |
|                 tr = base.ImportReference(tr);
 | |
|                 writeMr = wh.GetOrCreateWriteMethodReference(tr);
 | |
|                 return (writeMr != null);
 | |
|             }
 | |
| 
 | |
|             return true;
 | |
|         }
 | |
| 
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Creates a writer for an enum.
 | |
|         /// </summary>
 | |
|         /// <param name="enumTr"></param>
 | |
|         /// <returns></returns>
 | |
|         private MethodReference CreateEnumWriterMethodDefinition(TypeReference enumTr)
 | |
|         {
 | |
|             WriterProcessor wh = base.GetClass<WriterProcessor>();
 | |
| 
 | |
|             MethodDefinition createdWriterMd = CreateStaticWriterStubMethodDefinition(enumTr);
 | |
|             AddToStaticWriters(enumTr, createdWriterMd);
 | |
| 
 | |
|             ILProcessor processor = createdWriterMd.Body.GetILProcessor();
 | |
| 
 | |
|             //Element type for enum. EG: byte int ect
 | |
|             TypeReference underlyingTypeRef = enumTr.CachedResolve(base.Session).GetEnumUnderlyingTypeReference();
 | |
|             //Method to write that type.
 | |
|             MethodReference underlyingWriterMethodRef = wh.GetOrCreateWriteMethodReference(underlyingTypeRef);
 | |
|             if (underlyingWriterMethodRef == null)
 | |
|                 return null;
 | |
| 
 | |
|             ParameterDefinition writerParameterDef = createdWriterMd.Parameters[0];
 | |
|             ParameterDefinition valueParameterDef = createdWriterMd.Parameters[1];
 | |
|             //Push writer and value into call.
 | |
|             processor.Emit(OpCodes.Ldarg, writerParameterDef);
 | |
|             processor.Emit(OpCodes.Ldarg, valueParameterDef);
 | |
|             if (wh.IsAutoPackedType(underlyingTypeRef))
 | |
|                 processor.Emit(OpCodes.Ldc_I4, (int)AutoPackType.Packed);
 | |
| 
 | |
|             //writer.WriteXXX(value)
 | |
|             processor.Emit(OpCodes.Call, underlyingWriterMethodRef);
 | |
| 
 | |
|             processor.Emit(OpCodes.Ret);
 | |
|             return base.ImportReference(createdWriterMd);
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Calls an instanced writer from a static writer.
 | |
|         /// </summary>
 | |
|         private void CallInstancedWriter(MethodDefinition staticWriterMd, MethodReference instancedWriterMr)
 | |
|         {
 | |
|             ParameterDefinition writerPd = staticWriterMd.Parameters[0];
 | |
|             ParameterDefinition valuePd = staticWriterMd.Parameters[1];
 | |
|             ILProcessor processor = staticWriterMd.Body.GetILProcessor();
 | |
|             processor.Emit(OpCodes.Ldarg, writerPd);
 | |
|             processor.Emit(OpCodes.Ldarg, valuePd);
 | |
|             processor.Emit(instancedWriterMr.GetCallOpCode(base.Session), instancedWriterMr);
 | |
|             processor.Emit(OpCodes.Ret);
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Creates a writer for an array.
 | |
|         /// </summary>
 | |
|         private MethodReference CreateArrayWriterMethodReference(TypeReference objectTr)
 | |
|         {
 | |
|             WriterImports wi = base.GetClass<WriterImports>();
 | |
|             TypeReference valueTr = objectTr.GetElementType();
 | |
| 
 | |
|             //Write not found.
 | |
|             if (GetOrCreateWriteMethodReference(valueTr) == null)
 | |
|                 return null;
 | |
| 
 | |
|             MethodDefinition createdMd = CreateStaticWriterStubMethodDefinition(objectTr);
 | |
|             AddToStaticWriters(objectTr, createdMd);
 | |
| 
 | |
|             //Find instanced writer to use.
 | |
|             MethodReference instancedWriteMr = wi.Writer_WriteArray_MethodRef;
 | |
|             //Make generic.
 | |
|             GenericInstanceMethod writeGim = instancedWriteMr.MakeGenericMethod(new TypeReference[] { valueTr });
 | |
|             CallInstancedWriter(createdMd, writeGim);
 | |
| 
 | |
|             return base.ImportReference(createdMd);
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Creates a writer for a variety of generic collections.
 | |
|         /// </summary>
 | |
|         private MethodReference CreateGenericCollectionWriterMethodReference(TypeReference objectTr, SerializerType st)
 | |
|         {
 | |
|             WriterImports wi = base.GetClass<WriterImports>();
 | |
|             //Make value field generic.
 | |
|             GenericInstanceType genericInstance = (GenericInstanceType)objectTr;
 | |
|             base.ImportReference(genericInstance);
 | |
|             TypeReference valueTr = genericInstance.GenericArguments[0];
 | |
|             
 | |
|             List<TypeReference> genericArguments = new List<TypeReference>();
 | |
|             //Make sure all arguments have writers.
 | |
|             foreach (TypeReference gaTr in genericInstance.GenericArguments)
 | |
|             {
 | |
|                 MethodReference mr = GetOrCreateWriteMethodReference(gaTr);
 | |
|                 //Writer not found.
 | |
|                 if (mr == null)
 | |
|                 {
 | |
|                     base.LogError($"Writer could not be found or created for type {gaTr.FullName}.");
 | |
|                     return null;
 | |
|                 }
 | |
| 
 | |
|                 genericArguments.Add(gaTr);
 | |
|             }
 | |
|             MethodReference valueWriteMr = GetOrCreateWriteMethodReference(valueTr);
 | |
|             if (valueWriteMr == null)
 | |
|                 return null;
 | |
| 
 | |
|             MethodDefinition createdMd = CreateStaticWriterStubMethodDefinition(objectTr);
 | |
|             AddToStaticWriters(objectTr, createdMd);
 | |
| 
 | |
|             //Find instanced writer to use.
 | |
|             MethodReference instancedWriteMr;
 | |
|             if (st == SerializerType.Dictionary)
 | |
|                 instancedWriteMr = wi.Writer_WriteDictionary_MethodRef;
 | |
|             else if (st == SerializerType.List)
 | |
|                 instancedWriteMr = wi.Writer_WriteList_MethodRef;
 | |
|             else if (st == SerializerType.ListCache)
 | |
|                 instancedWriteMr = wi.Writer_WriteListCache_MethodRef;
 | |
|             else
 | |
|                 instancedWriteMr = null;
 | |
| 
 | |
|             //Not found.
 | |
|             if (instancedWriteMr == null)
 | |
|             {
 | |
|                 base.LogError($"Instanced writer not found for SerializerType {st} on object {objectTr.Name}.");
 | |
|                 return null;
 | |
|             }
 | |
| 
 | |
|             //Make generic.
 | |
|             GenericInstanceMethod writeGim = instancedWriteMr.MakeGenericMethod(genericArguments.ToArray());
 | |
|             CallInstancedWriter(createdMd, writeGim);
 | |
| 
 | |
|             return base.ImportReference(createdMd);
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Creates a method definition stub for objectTypeRef.
 | |
|         /// </summary>
 | |
|         /// <param name="objectTypeRef"></param>
 | |
|         /// <returns></returns>
 | |
|         public MethodDefinition CreateStaticWriterStubMethodDefinition(TypeReference objectTypeRef, string nameExtension = WriterProcessor.GENERATED_WRITER_NAMESPACE)
 | |
|         {
 | |
|             string methodName = $"{GENERATED_WRITE_PREFIX}{objectTypeRef.FullName}{nameExtension}";
 | |
|             // create new writer for this type
 | |
|             TypeDefinition writerTypeDef = base.GetClass<GeneralHelper>().GetOrCreateClass(out _, GENERATED_TYPE_ATTRIBUTES, GENERATED_WRITERS_CLASS_NAME, null);
 | |
| 
 | |
|             MethodDefinition writerMethodDef = writerTypeDef.AddMethod(methodName,
 | |
|                     MethodAttributes.Public |
 | |
|                     MethodAttributes.Static |
 | |
|                     MethodAttributes.HideBySig);
 | |
| 
 | |
|             base.GetClass<GeneralHelper>().CreateParameter(writerMethodDef, base.GetClass<WriterImports>().Writer_TypeRef, "writer");
 | |
|             base.GetClass<GeneralHelper>().CreateParameter(writerMethodDef, objectTypeRef, "value");
 | |
|             base.GetClass<GeneralHelper>().MakeExtensionMethod(writerMethodDef);
 | |
|             writerMethodDef.Body.InitLocals = true;
 | |
| 
 | |
|             return writerMethodDef;
 | |
|         }
 | |
|         #endregion
 | |
| 
 | |
|     }
 | |
| } |