using Mono.Cecil; using Mono.Cecil.Cil; using Mono.Cecil.Rocks; using UnrealSharpWeaver.MetaData; namespace UnrealSharpWeaver.Utilities; public static class MethodUtilities { public static readonly EFunctionFlags RpcFlags = EFunctionFlags.NetServer | EFunctionFlags.NetClient | EFunctionFlags.NetMulticast; public static readonly string UFunctionAttribute = "UFunctionAttribute"; /// name the method copy will have /// original method /// Add the method copy to the declaring type. this allows to use the original sources to be matched to the copy. /// /// new instance of as copy of the original public static MethodDefinition CopyMethod(string name, MethodDefinition method, bool addMethod = true, bool copyMetadataToken = true) { MethodDefinition newMethod = new MethodDefinition(name, method.Attributes, method.ReturnType) { HasThis = true, ExplicitThis = method.ExplicitThis, CallingConvention = method.CallingConvention, Body = method.Body }; if (copyMetadataToken) { newMethod.MetadataToken = method.MetadataToken; } foreach (ParameterDefinition parameter in method.Parameters) { TypeReference importedType = parameter.ParameterType.ImportType(); newMethod.Parameters.Add(new ParameterDefinition(parameter.Name, parameter.Attributes, importedType)); } if (addMethod) { method.DeclaringType.Methods.Add(newMethod); } return newMethod; } public static VariableDefinition AddLocalVariable(this MethodDefinition method, TypeReference typeReference) { var variable = new VariableDefinition(typeReference); method.Body.Variables.Add(variable); method.Body.InitLocals = true; return variable; } public static void FinalizeMethod(this MethodDefinition method) { method.Body.GetILProcessor().Emit(OpCodes.Ret); OptimizeMethod(method); } public static bool MethodIsCompilerGenerated(this ICustomAttributeProvider method) { return method.CustomAttributes.FindAttributeByType("System.Runtime.CompilerServices", "CompilerGeneratedAttribute") != null; } public static EFunctionFlags GetFunctionFlags(this MethodDefinition method) { EFunctionFlags flags = (EFunctionFlags) BaseMetaData.GetFlags(method, "FunctionFlagsMapAttribute"); if (method.IsPublic) { flags |= EFunctionFlags.Public; } else if (method.IsFamily) { flags |= EFunctionFlags.Protected; } else { flags |= EFunctionFlags.Private; } if (method.IsStatic) { flags |= EFunctionFlags.Static; } if (flags.HasAnyFlags(RpcFlags)) { flags |= EFunctionFlags.Net; if (!method.ReturnsVoid()) { throw new InvalidUnrealFunctionException(method, "RPCs can't have return values."); } if (flags.HasFlag(EFunctionFlags.BlueprintNativeEvent)) { throw new InvalidUnrealFunctionException(method, "BlueprintEvents methods cannot be replicated!"); } } // This represents both BlueprintNativeEvent and BlueprintImplementableEvent if (flags.HasFlag(EFunctionFlags.BlueprintNativeEvent)) { flags |= EFunctionFlags.Event; } // Native is needed to bind the function pointer of the UFunction to our own invoke in UE. return flags | EFunctionFlags.Native; } public static void OptimizeMethod(this MethodDefinition method) { if (method.Body.CodeSize == 0) { return; } method.Body.Optimize(); method.Body.SimplifyMacros(); } public static void RemoveReturnInstruction(this MethodDefinition method) { if (method.Body.Instructions.Count > 0 && method.Body.Instructions[^1].OpCode == OpCodes.Ret) { method.Body.Instructions.RemoveAt(method.Body.Instructions.Count - 1); } } public static CustomAttribute? GetUFunction(this MethodDefinition function) { return function.CustomAttributes.FindAttributeByType(WeaverImporter.UnrealSharpAttributesNamespace, UFunctionAttribute); } public static bool IsUFunction(this MethodDefinition method) { return GetUFunction(method) != null; } public static MethodReference ImportMethod(this MethodReference method) { return WeaverImporter.Instance.CurrentWeavingAssembly.MainModule.ImportReference(method); } }