using FishNet.CodeGenerating.Helping; using FishNet.CodeGenerating.Helping.Extension; using MonoFN.Cecil; using MonoFN.Cecil.Cil; using MonoFN.Cecil.Rocks; using MonoFN.Collections.Generic; using System.Collections.Generic; namespace FishNet.CodeGenerating.Extension { internal static class MethodDefinitionExtensions { public const MethodAttributes PUBLIC_VIRTUAL_ATTRIBUTES = (MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig); public const MethodAttributes PROTECTED_VIRTUAL_ATTRIBUTES = (MethodAttributes.Family | MethodAttributes.Virtual | MethodAttributes.HideBySig); /// /// Returns a custom attribute. /// public static CustomAttribute GetCustomAttribute(this MethodDefinition md, string attributeFullName) { if (md == null) return null; foreach (CustomAttribute item in md.CustomAttributes) { if (item.AttributeType.FullName == attributeFullName) return item; } //Not found. return null; } /// /// Clears the method content and returns ret. /// internal static void ClearMethodWithRet(this MethodDefinition md, CodegenSession session, ModuleDefinition importReturnModule = null) { md.Body.Instructions.Clear(); ILProcessor processor = md.Body.GetILProcessor(); processor.Add(session.GetClass().CreateRetDefault(md, importReturnModule)); } /// /// Returns the ParameterDefinition index from end of parameters. /// /// /// /// internal static ParameterDefinition GetEndParameter(this MethodDefinition md, int index) { //Not enough parameters. if (md.Parameters.Count < (index + 1)) return null; return md.Parameters[md.Parameters.Count - (index + 1)]; } /// /// Creates a variable type within the body and returns it's VariableDef. /// internal static VariableDefinition CreateVariable(this 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 static VariableDefinition CreateVariable(this MethodDefinition methodDef, CodegenSession session, System.Type variableType) { return CreateVariable(methodDef, session.GetClass().GetTypeReference(variableType)); } /// /// Returns the proper OpCode to use for call methods. /// public static MonoFN.Cecil.Cil.OpCode GetCallOpCode(this MethodDefinition md) { if (md.Attributes.HasFlag(MethodAttributes.Virtual)) return MonoFN.Cecil.Cil.OpCodes.Callvirt; else return MonoFN.Cecil.Cil.OpCodes.Call; } /// /// Returns the proper OpCode to use for call methods. /// public static MonoFN.Cecil.Cil.OpCode GetCallOpCode(this MethodReference mr, CodegenSession session) { return mr.CachedResolve(session).GetCallOpCode(); } /// /// Adds a parameter and returns added parameters. /// public static ParameterDefinition CreateParameter(this MethodDefinition thisMr, CodegenSession session, ParameterAttributes attr, System.Type type) { TypeReference parameterTypeRef = session.ImportReference(type); ParameterDefinition pd = new ParameterDefinition($"p{thisMr.Parameters.Count}", attr, parameterTypeRef); thisMr.Parameters.Add(pd); return pd; } /// /// Adds otherMd parameters to thisMR and returns added parameters. /// public static List CreateParameters(this MethodReference thisMr, CodegenSession session, MethodDefinition otherMd) { return thisMr.CachedResolve(session).CreateParameters(session, otherMd); } /// /// Adds otherMr parameters to thisMR and returns added parameters. /// public static List CreateParameters(this MethodReference thisMr, CodegenSession session, MethodReference otherMr) { return thisMr.CachedResolve(session).CreateParameters(session, otherMr.CachedResolve(session)); } /// /// Adds otherMd parameters to thisMd and returns added parameters. /// public static List CreateParameters(this MethodDefinition thisMd, CodegenSession session, MethodDefinition otherMd) { List results = new List(); foreach (ParameterDefinition pd in otherMd.Parameters) { session.ImportReference(pd.ParameterType); int currentCount = thisMd.Parameters.Count; string name = (pd.Name + currentCount); ParameterDefinition parameterDef = new ParameterDefinition(name, pd.Attributes, pd.ParameterType); //Set any default values. parameterDef.Constant = pd.Constant; parameterDef.IsReturnValue = pd.IsReturnValue; parameterDef.IsOut = pd.IsOut; foreach (CustomAttribute item in pd.CustomAttributes) parameterDef.CustomAttributes.Add(item); parameterDef.HasConstant = pd.HasConstant; parameterDef.HasDefault = pd.HasDefault; thisMd.Parameters.Add(parameterDef); results.Add(parameterDef); } return results; } /// /// Returns a method reference while considering if declaring type is generic. /// public static MethodReference GetMethodReference(this MethodDefinition md, CodegenSession session) { MethodReference methodRef = session.ImportReference(md); //Is generic. if (md.DeclaringType.HasGenericParameters) { GenericInstanceType git = methodRef.DeclaringType.MakeGenericInstanceType(); MethodReference result = new MethodReference(md.Name, md.ReturnType) { HasThis = md.HasThis, ExplicitThis = md.ExplicitThis, DeclaringType = git, CallingConvention = md.CallingConvention, }; foreach (ParameterDefinition pd in md.Parameters) { session.ImportReference(pd.ParameterType); result.Parameters.Add(pd); } return result; } else { return methodRef; } } /// /// Returns a method reference for a generic method. /// public static MethodReference GetMethodReference(this MethodDefinition md, CodegenSession session, TypeReference typeReference) { MethodReference methodRef = session.ImportReference(md); return methodRef.GetMethodReference(session, typeReference); } /// /// Removes ret if it exist at the end of the method. Returns if ret was removed. /// internal static bool RemoveEndRet(this MethodDefinition md, CodegenSession session) { int count = md.Body.Instructions.Count; if (count > 0 && md.Body.Instructions[count - 1].OpCode == OpCodes.Ret) { md.Body.Instructions.RemoveAt(count - 1); return true; } else { return false; } } /// /// Returns a method reference for a generic method. /// public static MethodReference GetMethodReference(this MethodDefinition md, CodegenSession session, TypeReference[] typeReferences) { MethodReference methodRef = session.ImportReference(md); return methodRef.GetMethodReference(session, typeReferences); } /// /// Makes a method definition public. /// public static void SetPublicAttributes(this MethodDefinition md) { md.Attributes = PUBLIC_VIRTUAL_ATTRIBUTES; } public static void SetProtectedAttributes(this MethodDefinition md) { md.Attributes = PROTECTED_VIRTUAL_ATTRIBUTES; } } }