forked from cgvr/DeltaVR
		
	
		
			
				
	
	
		
			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
 | 
						|
 | 
						|
    }
 | 
						|
} |