Files
TheDeclineOfWarriors/Assets/FishNet/CodeGenerating/Helpers/GeneralHelper.cs
T
2026-03-30 20:11:57 +07:00

1472 lines
63 KiB
C#

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