using FishNet.CodeGenerating.Helping.Extension; using FishNet.CodeGenerating.ILCore; using FishNet.Managing; using FishNet.Managing.Logging; using FishNet.Object; using FishNet.Object.Helping; using FishNet.Serializing; using FishNet.Serializing.Helping; using MonoFN.Cecil; using MonoFN.Cecil.Cil; using System; using System.Collections.Generic; using UnityEngine; using SR = System.Reflection; namespace FishNet.CodeGenerating.Helping { internal class GeneralHelper { #region Reflection references. internal string CodegenExcludeAttribute_FullName; internal MethodReference Queue_Enqueue_MethodRef; internal MethodReference Queue_get_Count_MethodRef; internal MethodReference Queue_Dequeue_MethodRef; internal MethodReference Queue_Clear_MethodRef; internal TypeReference List_TypeRef; internal MethodReference List_Clear_MethodRef; internal MethodReference List_get_Item_MethodRef; internal MethodReference List_get_Count_MethodRef; internal MethodReference List_Add_MethodRef; internal MethodReference List_RemoveRange_MethodRef; private MethodReference InstanceFinder_NetworkManager_MethodRef; private MethodReference NetworkBehaviour_CanLog_MethodRef; private MethodReference NetworkManager_CanLog_MethodRef; private MethodReference NetworkBehaviour_NetworkManager_MethodRef; private MethodReference NetworkManager_LogCommon_MethodRef; private MethodReference NetworkManager_LogWarning_MethodRef; private MethodReference NetworkManager_LogError_MethodRef; internal MethodReference Debug_LogCommon_MethodRef; internal MethodReference Debug_LogWarning_MethodRef; internal MethodReference Debug_LogError_MethodRef; internal MethodReference Comparers_EqualityCompare_MethodRef; internal MethodReference Comparers_IsDefault_MethodRef; internal MethodReference IsServer_MethodRef; internal MethodReference IsClient_MethodRef; internal MethodReference NetworkObject_Deinitializing_MethodRef; internal MethodReference Application_IsPlaying_MethodRef; private Dictionary _importedTypeReferences = new Dictionary(); private Dictionary _importedFieldReferences = new Dictionary(); private Dictionary _methodReferenceResolves = new Dictionary(); private Dictionary _typeReferenceResolves = new Dictionary(); private Dictionary _fieldReferenceResolves = new Dictionary(); private string NonSerialized_Attribute_FullName; private string Single_FullName; #endregion #region Const. public const string UNITYENGINE_ASSEMBLY_PREFIX = "UnityEngine."; #endregion internal bool ImportReferences() { Type tmpType; SR.MethodInfo tmpMi; SR.PropertyInfo tmpPi; NonSerialized_Attribute_FullName = typeof(NonSerializedAttribute).FullName; Single_FullName = typeof(float).FullName; CodegenExcludeAttribute_FullName = typeof(CodegenExcludeAttribute).FullName; tmpType = typeof(Queue<>); CodegenSession.ImportReference(tmpType); tmpMi = tmpType.GetMethod("get_Count"); Queue_get_Count_MethodRef = CodegenSession.ImportReference(tmpMi); foreach (SR.MethodInfo mi in tmpType.GetMethods()) { if (mi.Name == nameof(Queue.Enqueue)) Queue_Enqueue_MethodRef = CodegenSession.ImportReference(mi); else if (mi.Name == nameof(Queue.Dequeue)) Queue_Dequeue_MethodRef = CodegenSession.ImportReference(mi); else if (mi.Name == nameof(Queue.Clear)) Queue_Clear_MethodRef = CodegenSession.ImportReference(mi); } Type comparers = typeof(Comparers); foreach (SR.MethodInfo mi in comparers.GetMethods()) { if (mi.Name == nameof(Comparers.EqualityCompare)) Comparers_EqualityCompare_MethodRef = CodegenSession.ImportReference(mi); else if (mi.Name == nameof(Comparers.IsDefault)) Comparers_IsDefault_MethodRef = CodegenSession.ImportReference(mi); } //Misc. tmpType = typeof(UnityEngine.Application); tmpPi = tmpType.GetProperty(nameof(UnityEngine.Application.isPlaying)); if (tmpPi != null) Application_IsPlaying_MethodRef = CodegenSession.ImportReference(tmpPi.GetMethod); //Networkbehaviour. Type networkBehaviourType = typeof(NetworkBehaviour); foreach (SR.MethodInfo methodInfo in networkBehaviourType.GetMethods()) { if (methodInfo.Name == nameof(NetworkBehaviour.CanLog)) NetworkBehaviour_CanLog_MethodRef = CodegenSession.ImportReference(methodInfo); } foreach (SR.PropertyInfo propertyInfo in networkBehaviourType.GetProperties()) { if (propertyInfo.Name == nameof(NetworkBehaviour.NetworkManager)) NetworkBehaviour_NetworkManager_MethodRef = CodegenSession.ImportReference(propertyInfo.GetMethod); } //Instancefinder. Type instanceFinderType = typeof(InstanceFinder); SR.PropertyInfo getNetworkManagerPropertyInfo = instanceFinderType.GetProperty(nameof(InstanceFinder.NetworkManager)); InstanceFinder_NetworkManager_MethodRef = CodegenSession.ImportReference(getNetworkManagerPropertyInfo.GetMethod); //NetworkManager debug logs. Type networkManagerType = typeof(NetworkManager); foreach (SR.MethodInfo methodInfo in networkManagerType.GetMethods()) { if (methodInfo.Name == nameof(NetworkManager.Log)) NetworkManager_LogCommon_MethodRef = CodegenSession.ImportReference(methodInfo); else if (methodInfo.Name == nameof(NetworkManager.LogWarning)) NetworkManager_LogWarning_MethodRef = CodegenSession.ImportReference(methodInfo); else if (methodInfo.Name == nameof(NetworkManager.LogError)) NetworkManager_LogError_MethodRef = CodegenSession.ImportReference(methodInfo); else if (methodInfo.Name == nameof(NetworkManager.CanLog)) NetworkManager_CanLog_MethodRef = CodegenSession.ImportReference(methodInfo); } //Lists. tmpType = typeof(List<>); List_TypeRef = CodegenSession.ImportReference(tmpType); SR.MethodInfo lstMi; lstMi = tmpType.GetMethod("Add"); List_Add_MethodRef = CodegenSession.ImportReference(lstMi); lstMi = tmpType.GetMethod("RemoveRange"); List_RemoveRange_MethodRef = CodegenSession.ImportReference(lstMi); lstMi = tmpType.GetMethod("get_Count"); List_get_Count_MethodRef = CodegenSession.ImportReference(lstMi); lstMi = tmpType.GetMethod("get_Item"); List_get_Item_MethodRef = CodegenSession.ImportReference(lstMi); lstMi = tmpType.GetMethod("Clear"); List_Clear_MethodRef = CodegenSession.ImportReference(lstMi); //Unity debug logs. Type debugType = typeof(UnityEngine.Debug); foreach (SR.MethodInfo methodInfo in debugType.GetMethods()) { if (methodInfo.Name == nameof(Debug.LogWarning) && methodInfo.GetParameters().Length == 1) Debug_LogWarning_MethodRef = CodegenSession.ImportReference(methodInfo); else if (methodInfo.Name == nameof(Debug.LogError) && methodInfo.GetParameters().Length == 1) Debug_LogError_MethodRef = CodegenSession.ImportReference(methodInfo); else if (methodInfo.Name == nameof(Debug.Log) && methodInfo.GetParameters().Length == 1) Debug_LogCommon_MethodRef = CodegenSession.ImportReference(methodInfo); } Type codegenHelper = typeof(CodegenHelper); foreach (SR.MethodInfo methodInfo in codegenHelper.GetMethods()) { if (methodInfo.Name == nameof(CodegenHelper.NetworkObject_Deinitializing)) NetworkObject_Deinitializing_MethodRef = CodegenSession.ImportReference(methodInfo); else if (methodInfo.Name == nameof(CodegenHelper.IsClient)) IsClient_MethodRef = CodegenSession.ImportReference(methodInfo); else if (methodInfo.Name == nameof(CodegenHelper.IsServer)) IsServer_MethodRef = CodegenSession.ImportReference(methodInfo); } return true; } #region Resolves. /// /// Adds a typeRef to TypeReferenceResolves. /// internal void AddTypeReferenceResolve(TypeReference typeRef, TypeDefinition typeDef) { _typeReferenceResolves[typeRef] = typeDef; } /// /// Gets a TypeDefinition for typeRef. /// internal TypeDefinition GetTypeReferenceResolve(TypeReference typeRef) { TypeDefinition result; if (_typeReferenceResolves.TryGetValue(typeRef, out result)) { return result; } else { result = typeRef.Resolve(); AddTypeReferenceResolve(typeRef, result); } return result; } /// /// Adds a methodRef to MethodReferenceResolves. /// internal void AddMethodReferenceResolve(MethodReference methodRef, MethodDefinition methodDef) { _methodReferenceResolves[methodRef] = methodDef; } /// /// Gets a TypeDefinition for typeRef. /// internal MethodDefinition GetMethodReferenceResolve(MethodReference methodRef) { MethodDefinition result; if (_methodReferenceResolves.TryGetValue(methodRef, out result)) { return result; } else { result = methodRef.Resolve(); AddMethodReferenceResolve(methodRef, result); } return result; } /// /// Adds a fieldRef to FieldReferenceResolves. /// internal void AddFieldReferenceResolve(FieldReference fieldRef, FieldDefinition fieldDef) { _fieldReferenceResolves[fieldRef] = fieldDef; } /// /// Gets a FieldDefinition for fieldRef. /// internal FieldDefinition GetFieldReferenceResolve(FieldReference fieldRef) { FieldDefinition result; if (_fieldReferenceResolves.TryGetValue(fieldRef, out result)) { return result; } else { result = fieldRef.Resolve(); AddFieldReferenceResolve(fieldRef, result); } return result; } #endregion /// /// Returns if typeDef should be ignored. /// /// /// internal bool IgnoreTypeDefinition(TypeDefinition typeDef) { //If FishNet assembly. if (typeDef.Module.Assembly.Name.Name == FishNetILPP.RUNTIME_ASSEMBLY_NAME) { foreach (CustomAttribute item in typeDef.CustomAttributes) { if (item.AttributeType.FullName == typeof(CodegenIncludeInternalAttribute).FullName) { if (FishNetILPP.CODEGEN_THIS_NAMESPACE.Length > 0) return !typeDef.FullName.Contains(FishNetILPP.CODEGEN_THIS_NAMESPACE); else return false; } } return true; } //Not FishNet assembly. else { if (FishNetILPP.CODEGEN_THIS_NAMESPACE.Length > 0) return true; foreach (CustomAttribute item in typeDef.CustomAttributes) { if (item.AttributeType.FullName == typeof(CodegenExcludeAttribute).FullName) return true; } return false; } } /// /// Returns if type uses CodegenExcludeAttribute. /// internal bool CodegenExclude(SR.MethodInfo methodInfo) { foreach (SR.CustomAttributeData item in methodInfo.CustomAttributes) { if (item.AttributeType == typeof(CodegenExcludeAttribute)) return true; } return false; } /// /// Returns if type uses CodegenExcludeAttribute. /// internal bool CodegenExclude(MethodDefinition methodDef) { foreach (CustomAttribute item in methodDef.CustomAttributes) { if (item.AttributeType.FullName == CodegenExcludeAttribute_FullName) return true; } return false; } /// /// Returns if type uses CodegenExcludeAttribute. /// internal bool CodegenExclude(FieldDefinition fieldDef) { foreach (CustomAttribute item in fieldDef.CustomAttributes) { if (item.AttributeType.FullName == CodegenExcludeAttribute_FullName) return true; } return false; } /// /// Returns if type uses CodegenExcludeAttribute. /// internal bool CodegenExclude(PropertyDefinition propDef) { foreach (CustomAttribute item in propDef.CustomAttributes) { if (item.AttributeType.FullName == CodegenExcludeAttribute_FullName) return true; } return false; } /// /// Calls copiedMd with the assumption md shares the same parameters. /// internal void CallCopiedMethod(MethodDefinition md, MethodDefinition copiedMd) { ILProcessor processor = md.Body.GetILProcessor(); processor.Emit(OpCodes.Ldarg_0); foreach (var item in copiedMd.Parameters) processor.Emit(OpCodes.Ldarg, item); processor.Emit(OpCodes.Call, copiedMd); } /// /// Copies one method to another while transferring diagnostic paths. /// internal MethodDefinition CopyMethod(MethodDefinition originalMd, string toName, out bool alreadyCreated) { TypeDefinition typeDef = originalMd.DeclaringType; MethodDefinition copyMd = typeDef.GetMethod(toName); //Already made. if (copyMd != null) { alreadyCreated = true; return copyMd; } else { alreadyCreated = false; } //Create the method body. copyMd = new MethodDefinition( toName, originalMd.Attributes, originalMd.ReturnType); typeDef.Methods.Add(copyMd); copyMd.Body.InitLocals = true; //Copy parameter expecations into new method. foreach (ParameterDefinition pd in originalMd.Parameters) copyMd.Parameters.Add(pd); //Swap bodies. (copyMd.Body, originalMd.Body) = (originalMd.Body, copyMd.Body); //Move over all the debugging information foreach (SequencePoint sequencePoint in originalMd.DebugInformation.SequencePoints) copyMd.DebugInformation.SequencePoints.Add(sequencePoint); originalMd.DebugInformation.SequencePoints.Clear(); foreach (CustomDebugInformation customInfo in originalMd.CustomDebugInformations) copyMd.CustomDebugInformations.Add(customInfo); originalMd.CustomDebugInformations.Clear(); //Swap debuginformation scope. (originalMd.DebugInformation.Scope, copyMd.DebugInformation.Scope) = (copyMd.DebugInformation.Scope, originalMd.DebugInformation.Scope); return copyMd; } /// /// Creates the RuntimeInitializeOnLoadMethod attribute for a method. /// internal void CreateRuntimeInitializeOnLoadMethodAttribute(MethodDefinition methodDef, string loadType = "") { TypeReference attTypeRef = GetTypeReference(typeof(RuntimeInitializeOnLoadMethodAttribute)); foreach (CustomAttribute item in methodDef.CustomAttributes) { //Already exist. if (item.AttributeType.FullName == attTypeRef.FullName) return; } int parameterRequirement = (loadType.Length == 0) ? 0 : 1; MethodDefinition constructorMethodDef = attTypeRef.GetConstructor(parameterRequirement); MethodReference constructorMethodRef = CodegenSession.ImportReference(constructorMethodDef); CustomAttribute ca = new CustomAttribute(constructorMethodRef); /* If load type isn't null then it * has to be passed in as the first argument. */ if (loadType.Length > 0) { Type t = typeof(RuntimeInitializeLoadType); foreach (UnityEngine.RuntimeInitializeLoadType value in t.GetEnumValues()) { if (loadType == value.ToString()) { TypeReference tr = CodegenSession.ImportReference(t); CustomAttributeArgument arg = new CustomAttributeArgument(tr, value); ca.ConstructorArguments.Add(arg); } } } methodDef.CustomAttributes.Add(ca); } /// /// Gets the default AutoPackType to use for typeRef. /// /// /// internal AutoPackType GetDefaultAutoPackType(TypeReference typeRef) { //Singles are defauled to unpacked. if (typeRef.FullName == Single_FullName) return AutoPackType.Unpacked; else return AutoPackType.Packed; } /// /// Gets the InitializeOnce method in typeDef or creates the method should it not exist. /// /// /// internal MethodDefinition GetOrCreateMethod(TypeDefinition typeDef, out bool created, MethodAttributes methodAttr, string methodName, TypeReference returnType) { MethodDefinition result = typeDef.GetMethod(methodName); if (result == null) { created = true; result = new MethodDefinition(methodName, methodAttr, returnType); typeDef.Methods.Add(result); } else { created = false; } return result; } /// /// Gets a class within moduleDef or creates and returns the class if it does not already exist. /// /// /// internal TypeDefinition GetOrCreateClass(out bool created, TypeAttributes typeAttr, string className, TypeReference baseTypeRef) { TypeDefinition type = CodegenSession.Module.GetClass(className); if (type != null) { created = false; return type; } else { created = true; type = new TypeDefinition(FishNetILPP.RUNTIME_ASSEMBLY_NAME, className, typeAttr, CodegenSession.ImportReference(typeof(object))); //Add base class if specified. if (baseTypeRef != null) type.BaseType = CodegenSession.ImportReference(baseTypeRef); CodegenSession.Module.Types.Add(type); return type; } } #region HasNonSerializableAttribute /// /// Returns if fieldDef has a NonSerialized attribute. /// /// /// internal bool HasNonSerializableAttribute(FieldDefinition fieldDef) { foreach (CustomAttribute customAttribute in fieldDef.CustomAttributes) { if (customAttribute.AttributeType.FullName == NonSerialized_Attribute_FullName) return true; } //Fall through, no matches. return false; } /// /// Returns if typeDef has a NonSerialized attribute. /// /// /// internal bool HasNonSerializableAttribute(TypeDefinition typeDef) { foreach (CustomAttribute customAttribute in typeDef.CustomAttributes) { if (customAttribute.AttributeType.FullName == NonSerialized_Attribute_FullName) return true; } //Fall through, no matches. return false; } #endregion /// /// Gets a TypeReference for a type. /// /// internal TypeReference GetTypeReference(Type type) { TypeReference result; if (!_importedTypeReferences.TryGetValue(type, out result)) { result = CodegenSession.ImportReference(type); _importedTypeReferences.Add(type, result); } return result; } /// /// Gets a FieldReference for a type. /// /// internal FieldReference GetFieldReference(FieldDefinition fieldDef) { FieldReference result; if (!_importedFieldReferences.TryGetValue(fieldDef, out result)) { result = CodegenSession.ImportReference(fieldDef); _importedFieldReferences.Add(fieldDef, result); } return result; } /// /// Gets the current constructor for typeDef, or makes a new one if constructor doesn't exist. /// /// /// internal MethodDefinition GetOrCreateConstructor(TypeDefinition typeDef, out bool created, bool makeStatic) { // find constructor MethodDefinition constructorMethodDef = typeDef.GetMethod(".cctor"); if (constructorMethodDef == null) constructorMethodDef = typeDef.GetMethod(".ctor"); //Constructor already exist. if (constructorMethodDef != null) { if (!makeStatic) constructorMethodDef.Attributes &= ~MethodAttributes.Static; created = false; } //Static constructor does not exist yet. else { created = true; MethodAttributes methodAttr = (MonoFN.Cecil.MethodAttributes.HideBySig | MonoFN.Cecil.MethodAttributes.SpecialName | MonoFN.Cecil.MethodAttributes.RTSpecialName); if (makeStatic) methodAttr |= MonoFN.Cecil.MethodAttributes.Static; //Create a constructor. constructorMethodDef = new MethodDefinition(".ctor", methodAttr, typeDef.Module.TypeSystem.Void ); typeDef.Methods.Add(constructorMethodDef); //Add ret. ILProcessor processor = constructorMethodDef.Body.GetILProcessor(); processor.Emit(OpCodes.Ret); } return constructorMethodDef; } /// /// Creates a return of boolean type. /// /// /// internal void CreateRetBoolean(ILProcessor processor, bool result) { OpCode code = (result) ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0; processor.Emit(code); processor.Emit(OpCodes.Ret); } #region Debug logging. /// /// Creates a debug print if NetworkManager.CanLog is true. /// /// /// /// True to use InstanceFinder, false to use base. /// internal List CreateDebugWithCanLogInstructions(ILProcessor processor, string message, LoggingType loggingType, bool useStatic, bool useNetworkManagerLog) { List instructions = new List(); if (loggingType == LoggingType.Off) return instructions; List debugPrint = CreateDebugInstructions(processor, message, loggingType, useNetworkManagerLog); //Couldn't make debug print. if (debugPrint.Count == 0) return instructions; VariableDefinition networkManagerVd = CreateVariable(processor.Body.Method, typeof(NetworkManager)); //Using InstanceFinder(static). if (useStatic) { //Store instancefinder to nm variable. instructions.Add(processor.Create(OpCodes.Call, InstanceFinder_NetworkManager_MethodRef)); instructions.Add(processor.Create(OpCodes.Stloc, networkManagerVd)); } //Using networkBehaviour. else { //Store nm reference. instructions.Add(processor.Create(OpCodes.Ldarg_0)); instructions.Add(processor.Create(OpCodes.Call, NetworkBehaviour_NetworkManager_MethodRef)); instructions.Add(processor.Create(OpCodes.Stloc, networkManagerVd)); //If was set to null then try to log with instancefinder. Instruction skipStaticSetInst = processor.Create(OpCodes.Nop); //if (nmVd == null) nmVd = InstanceFinder.NetworkManager. instructions.Add(processor.Create(OpCodes.Ldloc, networkManagerVd)); instructions.Add(processor.Create(OpCodes.Brtrue_S, skipStaticSetInst)); //Store instancefinder to nm variable. instructions.Add(processor.Create(OpCodes.Call, InstanceFinder_NetworkManager_MethodRef)); instructions.Add(processor.Create(OpCodes.Stloc, networkManagerVd)); instructions.Add(skipStaticSetInst); } Instruction skipDebugInst = processor.Create(OpCodes.Nop); //null check nm reference. If null then skip logging. instructions.Add(processor.Create(OpCodes.Ldloc, networkManagerVd)); instructions.Add(processor.Create(OpCodes.Brfalse_S, skipDebugInst)); //Only need to call CanLog if not using networkmanager logging. if (!useNetworkManagerLog) { //Call canlog. instructions.Add(processor.Create(OpCodes.Ldarg_0)); instructions.Add(processor.Create(OpCodes.Ldc_I4, (int)loggingType)); instructions.Add(processor.Create(OpCodes.Call, NetworkBehaviour_CanLog_MethodRef)); instructions.Add(processor.Create(OpCodes.Brfalse_S, skipDebugInst)); } instructions.Add(processor.Create(OpCodes.Ldloc, networkManagerVd)); instructions.AddRange(debugPrint); instructions.Add(skipDebugInst); return instructions; } /// /// Creates a debug print if NetworkManager.CanLog is true. /// /// /// /// True to use InstanceFinder, false to use base. /// internal void CreateDebugWithCanLog(ILProcessor processor, string message, LoggingType loggingType, bool useStatic, bool useNetworkManagerLog) { List instructions = CreateDebugWithCanLogInstructions(processor, message, loggingType, useStatic, useNetworkManagerLog); if (instructions.Count == 0) return; processor.Add(instructions); } /// /// Creates a debug and returns instructions. /// /// private List CreateDebugInstructions(ILProcessor processor, string message, LoggingType loggingType, bool useNetworkManagerLog) { List instructions = new List(); if (loggingType == LoggingType.Off) { CodegenSession.LogError($"CreateDebug called with LoggingType.Off."); return instructions; } instructions.Add(processor.Create(OpCodes.Ldstr, message)); MethodReference methodRef; if (loggingType == LoggingType.Common) methodRef = (useNetworkManagerLog) ? NetworkManager_LogCommon_MethodRef : Debug_LogCommon_MethodRef; else if (loggingType == LoggingType.Warning) methodRef = (useNetworkManagerLog) ? NetworkManager_LogWarning_MethodRef : Debug_LogWarning_MethodRef; else methodRef = (useNetworkManagerLog) ? NetworkManager_LogError_MethodRef : Debug_LogError_MethodRef; instructions.Add(processor.Create(OpCodes.Call, methodRef)); return instructions; } #endregion #region CreateVariable / CreateParameter. /// /// Creates a parameter within methodDef and returns it's ParameterDefinition. /// /// /// /// internal ParameterDefinition CreateParameter(MethodDefinition methodDef, TypeDefinition parameterTypeDef, string name = "", ParameterAttributes attributes = ParameterAttributes.None, int index = -1) { TypeReference typeRef = methodDef.Module.ImportReference(parameterTypeDef); return CreateParameter(methodDef, typeRef, name, attributes, index); } /// /// Creates a parameter within methodDef and returns it's ParameterDefinition. /// /// /// /// internal ParameterDefinition CreateParameter(MethodDefinition methodDef, TypeReference parameterTypeRef, string name = "", ParameterAttributes attributes = ParameterAttributes.None, int index = -1) { ParameterDefinition parameterDef = new ParameterDefinition(name, attributes, parameterTypeRef); if (index == -1) methodDef.Parameters.Add(parameterDef); else methodDef.Parameters.Insert(index, parameterDef); return parameterDef; } /// /// Creates a parameter within methodDef and returns it's ParameterDefinition. /// /// /// /// internal ParameterDefinition CreateParameter(MethodDefinition methodDef, Type parameterType, string name = "", ParameterAttributes attributes = ParameterAttributes.None, int index = -1) { return CreateParameter(methodDef, GetTypeReference(parameterType), name, attributes, index); } /// /// Creates a variable type within the body and returns it's VariableDef. /// /// /// /// internal VariableDefinition CreateVariable(MethodDefinition methodDef, TypeReference variableTypeRef) { VariableDefinition variableDef = new VariableDefinition(variableTypeRef); methodDef.Body.Variables.Add(variableDef); return variableDef; } /// Creates a variable type within the body and returns it's VariableDef. /// /// /// /// /// internal VariableDefinition CreateVariable(MethodDefinition methodDef, Type variableType) { return CreateVariable(methodDef, GetTypeReference(variableType)); } #endregion #region SetVariableDef. /// /// Initializes variableDef as a new object or collection of typeDef. /// /// /// /// internal void SetVariableDefinitionFromObject(ILProcessor processor, VariableDefinition variableDef, TypeDefinition typeDef) { TypeReference type = variableDef.VariableType; if (type.IsValueType) { // structs are created with Initobj processor.Emit(OpCodes.Ldloca, variableDef); processor.Emit(OpCodes.Initobj, type); } else if (typeDef.InheritsFrom()) { MethodReference soCreateInstanceMr = processor.Body.Method.Module.ImportReference(() => UnityEngine.ScriptableObject.CreateInstance()); GenericInstanceMethod genericInstanceMethod = soCreateInstanceMr.GetElementMethod().MakeGenericMethod(new TypeReference[] { type }); processor.Emit(OpCodes.Call, genericInstanceMethod); processor.Emit(OpCodes.Stloc, variableDef); } else { MethodDefinition constructorMethodDef = type.GetConstructor(); if (constructorMethodDef == null) { CodegenSession.LogError($"{type.Name} can't be deserialized because a default constructor could not be found. Create a default constructor or a custom serializer/deserializer."); return; } MethodReference constructorMethodRef = processor.Body.Method.Module.ImportReference(constructorMethodDef); processor.Emit(OpCodes.Newobj, constructorMethodRef); processor.Emit(OpCodes.Stloc, variableDef); } } /// /// Assigns value to a VariableDef. /// /// /// /// internal void SetVariableDefinitionFromInt(ILProcessor processor, VariableDefinition variableDef, int value) { processor.Emit(OpCodes.Ldc_I4, value); processor.Emit(OpCodes.Stloc, variableDef); } /// /// Assigns value to a VariableDef. /// /// /// /// internal void SetVariableDefinitionFromParameter(ILProcessor processor, VariableDefinition variableDef, ParameterDefinition value) { processor.Emit(OpCodes.Ldarg, value); processor.Emit(OpCodes.Stloc, variableDef); } #endregion. /// /// Returns if an instruction is a call to a method. /// /// /// /// internal bool IsCallToMethod(Instruction instruction, out MethodDefinition calledMethod) { if (instruction.OpCode == OpCodes.Call && instruction.Operand is MethodDefinition method) { calledMethod = method; return true; } else { calledMethod = null; return false; } } /// /// Returns if a serializer and deserializer exist for typeRef. /// /// /// True to create if missing. /// internal bool HasSerializerAndDeserializer(TypeReference typeRef, bool create) { //Make sure it's imported into current module. typeRef = CodegenSession.ImportReference(typeRef); //Can be serialized/deserialized. bool hasWriter = CodegenSession.WriterHelper.HasSerializer(typeRef, create); bool hasReader = CodegenSession.ReaderHelper.HasDeserializer(typeRef, create); return (hasWriter && hasReader); } /// /// Creates a return of default value for methodDef. /// /// public List CreateRetDefault(MethodDefinition methodDef, ModuleDefinition importReturnModule = null) { ILProcessor processor = methodDef.Body.GetILProcessor(); List instructions = new List(); //If requires a value return. if (methodDef.ReturnType != methodDef.Module.TypeSystem.Void) { //Import type first. methodDef.Module.ImportReference(methodDef.ReturnType); if (importReturnModule != null) importReturnModule.ImportReference(methodDef.ReturnType); VariableDefinition vd = CodegenSession.GeneralHelper.CreateVariable(methodDef, methodDef.ReturnType); instructions.Add(processor.Create(OpCodes.Ldloca_S, vd)); instructions.Add(processor.Create(OpCodes.Initobj, vd.VariableType)); instructions.Add(processor.Create(OpCodes.Ldloc, vd)); } instructions.Add(processor.Create(OpCodes.Ret)); return instructions; } } }