306 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
		
		
			
		
	
	
			306 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
|  | using System; | |||
|  | using System.Collections.Generic; | |||
|  | using System.Linq; | |||
|  | using EpicGames.Core; | |||
|  | using EpicGames.UHT.Types; | |||
|  | using UnrealSharpScriptGenerator.Exporters; | |||
|  | using UnrealSharpScriptGenerator.PropertyTranslators; | |||
|  | 
 | |||
|  | namespace UnrealSharpScriptGenerator.Utilities; | |||
|  | 
 | |||
|  | public static class FunctionUtilities | |||
|  | { | |||
|  |     public static bool HasAnyFlags(this UhtFunction function, EFunctionFlags flags) | |||
|  |     { | |||
|  |         return (function.FunctionFlags & flags) != 0; | |||
|  |     } | |||
|  |      | |||
|  |     public static bool HasAllFlags(this UhtFunction function, EFunctionFlags flags) | |||
|  |     { | |||
|  |         return (function.FunctionFlags & flags) == flags; | |||
|  |     } | |||
|  | 
 | |||
|  |     public static bool IsInterfaceFunction(this UhtFunction function) | |||
|  |     { | |||
|  |         if (function.Outer is not UhtClass classOwner) | |||
|  |         { | |||
|  |             return false; | |||
|  |         } | |||
|  |          | |||
|  |         if (classOwner.HasAnyFlags(EClassFlags.Interface)) | |||
|  |         { | |||
|  |             return true; | |||
|  |         } | |||
|  | 
 | |||
|  |         string sourceName; | |||
|  |         if (function.SourceName.EndsWith("_Implementation")) | |||
|  |         { | |||
|  |             sourceName = function.SourceName.Substring(0, function.SourceName.Length - 15); | |||
|  |         } | |||
|  |         else | |||
|  |         { | |||
|  |             sourceName = function.SourceName; | |||
|  |         } | |||
|  |          | |||
|  |         UhtClass? currentClass = classOwner; | |||
|  |         while (currentClass != null) | |||
|  |         { | |||
|  |             foreach (UhtClass currentInterface in currentClass.GetInterfaces()) | |||
|  |             { | |||
|  |                 UhtClass? interfaceClass = currentInterface.GetInterfaceAlternateClass(); | |||
|  |                 if (interfaceClass != null && interfaceClass.FindFunctionByName(sourceName) != null) | |||
|  |                 { | |||
|  |                     return true; | |||
|  |                 } | |||
|  |             } | |||
|  |      | |||
|  |             currentClass = currentClass.Super as UhtClass; | |||
|  |         } | |||
|  | 
 | |||
|  |         return false; | |||
|  |     } | |||
|  |      | |||
|  |     public static bool HasOutParams(this UhtFunction function) | |||
|  |     { | |||
|  |         // Multicast delegates can have out params, but the UFunction flag isn't set. | |||
|  |         foreach (UhtProperty param in function.Properties) | |||
|  |         { | |||
|  |             if (param.HasAnyFlags(EPropertyFlags.OutParm)) | |||
|  |             { | |||
|  |                 return true; | |||
|  |             } | |||
|  |         } | |||
|  |         return false; | |||
|  |     } | |||
|  |      | |||
|  |     public static bool HasParametersOrReturnValue(this UhtFunction function) | |||
|  |     { | |||
|  |         return function.HasParameters || function.ReturnProperty != null; | |||
|  |     } | |||
|  |      | |||
|  |     public static string GetNativeFunctionName(this UhtFunction function) | |||
|  |     { | |||
|  |         return $"{function.SourceName}_NativeFunction"; | |||
|  |     } | |||
|  | 
 | |||
|  |     public static bool HasSameSignature(this UhtFunction function, UhtFunction otherFunction) | |||
|  |     { | |||
|  |         if (function.Children.Count != otherFunction.Children.Count) | |||
|  |         { | |||
|  |             return false; | |||
|  |         } | |||
|  |          | |||
|  |         for (int i = 0; i < function.Children.Count; i++) | |||
|  |         { | |||
|  |             UhtProperty param = (UhtProperty) function.Children[i]; | |||
|  |             UhtProperty otherParam = (UhtProperty) otherFunction.Children[i]; | |||
|  |             if (!param.IsSameType(otherParam)) | |||
|  |             { | |||
|  |                 return false; | |||
|  |             } | |||
|  |         } | |||
|  |         return true; | |||
|  |     } | |||
|  |      | |||
|  |     public static bool IsAutocast(this UhtFunction function) | |||
|  |     { | |||
|  |         if (!function.FunctionFlags.HasAllFlags(EFunctionFlags.Static) || function.ReturnProperty == null || function.Children.Count != 2) | |||
|  |         {             | |||
|  |             return false; | |||
|  |         } | |||
|  | 
 | |||
|  |         if (function.Properties.First() is not UhtStructProperty) | |||
|  |         { | |||
|  |             return false; | |||
|  |         } | |||
|  | 
 | |||
|  |         // These will be interfaces in C#, which implicit conversion doesn't work for. | |||
|  |         // TODO: Support these in the future. | |||
|  |         UhtProperty returnProperty = function.ReturnProperty!; | |||
|  |         if (returnProperty is UhtArrayProperty or UhtSetProperty or UhtMapProperty) | |||
|  |         { | |||
|  |             return false; | |||
|  |         } | |||
|  | 
 | |||
|  |         if (function.HasMetadata("BlueprintAutocast")) | |||
|  |         { | |||
|  |             return true; | |||
|  |         } | |||
|  |          | |||
|  |         string sourceName = function.SourceName; | |||
|  |         return sourceName.StartsWith("Conv_", StringComparison.OrdinalIgnoreCase) || sourceName.StartsWith("To"); | |||
|  |     } | |||
|  |      | |||
|  |     public static string GetBlueprintAutocastName(this UhtFunction function) | |||
|  |     { | |||
|  |         int toTypeIndex = function.SourceName.IndexOf("Conv_", StringComparison.Ordinal); | |||
|  |         return toTypeIndex == -1 ? function.SourceName : function.SourceName.Substring(toTypeIndex + 5); | |||
|  |     } | |||
|  |      | |||
|  |     private static bool IsBlueprintAccessor(this UhtFunction function, string accessorType, Func<UhtProperty, UhtFunction?> getBlueprintAccessor) | |||
|  |     { | |||
|  |         if (function.Properties.Count() != 1 ) | |||
|  |         { | |||
|  |             return false; | |||
|  |         } | |||
|  |          | |||
|  |         if (function.HasMetadata(accessorType)) | |||
|  |         { | |||
|  |             return true; | |||
|  |         } | |||
|  | 
 | |||
|  |         if (function.Outer is not UhtClass classObj) | |||
|  |         { | |||
|  |             return false; | |||
|  |         } | |||
|  |          | |||
|  |         foreach (UhtProperty property in classObj.Properties) | |||
|  |         { | |||
|  |             if (function != getBlueprintAccessor(property)! || !function.VerifyBlueprintAccessor(property)) | |||
|  |             { | |||
|  |                 continue; | |||
|  |             } | |||
|  |                  | |||
|  |             return true; | |||
|  |         } | |||
|  | 
 | |||
|  |         return false; | |||
|  |     } | |||
|  |      | |||
|  |     public static bool VerifyBlueprintAccessor(this UhtFunction function, UhtProperty property) | |||
|  |     { | |||
|  |         if (!function.Properties.Any() || function.Properties.Count() != 1) | |||
|  |         { | |||
|  |             return false; | |||
|  |         } | |||
|  |              | |||
|  |         UhtProperty firstProperty = function.Properties.First(); | |||
|  |         return firstProperty.IsSameType(property); | |||
|  |     } | |||
|  |      | |||
|  |     public static bool IsNativeAccessor(this UhtFunction function, GetterSetterMode accessorType) | |||
|  |     { | |||
|  |         UhtClass classObj = (function.Outer as UhtClass)!; | |||
|  |         foreach (UhtProperty property in classObj.Properties) | |||
|  |         { | |||
|  |             if (accessorType + property.EngineName == function.SourceName) | |||
|  |             { | |||
|  |                 switch (accessorType) | |||
|  |                 { | |||
|  |                     case GetterSetterMode.Get: | |||
|  |                         return property.HasNativeGetter(); | |||
|  |                     case GetterSetterMode.Set: | |||
|  |                         return property.HasNativeSetter(); | |||
|  |                 } | |||
|  |             } | |||
|  |         } | |||
|  |          | |||
|  |         return false; | |||
|  |     } | |||
|  |      | |||
|  |     public static bool IsAnyGetter(this UhtFunction function) | |||
|  |     { | |||
|  |         if (function.Properties.Count() != 1) | |||
|  |         { | |||
|  |             return false; | |||
|  |         } | |||
|  |          | |||
|  |         return function.IsBlueprintAccessor("BlueprintGetter", property => property.GetBlueprintGetter())  | |||
|  |                || function.IsNativeAccessor(GetterSetterMode.Get); | |||
|  |     } | |||
|  | 
 | |||
|  |     public static bool IsAnySetter(this UhtFunction function) | |||
|  |     { | |||
|  |         if (function.Properties.Count() != 1) | |||
|  |         { | |||
|  |             return false; | |||
|  |         } | |||
|  |          | |||
|  |         return function.IsBlueprintAccessor("BlueprintSetter", property => property.GetBlueprintSetter())  | |||
|  |                || function.IsNativeAccessor(GetterSetterMode.Set); | |||
|  |     } | |||
|  | 
 | |||
|  |     public static bool HasGenericTypeSupport(this UhtFunction function) | |||
|  |     { | |||
|  |         if (!function.HasMetadata("DeterminesOutputType")) return false; | |||
|  | 
 | |||
|  |         var propertyDOTEngineName = function.GetMetadata("DeterminesOutputType"); | |||
|  | 
 | |||
|  |         var propertyDeterminingOutputType = function.Properties | |||
|  |             .Where(p => p.EngineName == propertyDOTEngineName) | |||
|  |             .FirstOrDefault(); | |||
|  | 
 | |||
|  |         if (propertyDeterminingOutputType == null) return false; | |||
|  | 
 | |||
|  |         PropertyTranslator dotParamTranslator = PropertyTranslatorManager.GetTranslator(propertyDeterminingOutputType)!; | |||
|  |         if (!dotParamTranslator.CanSupportGenericType(propertyDeterminingOutputType)) return false; | |||
|  | 
 | |||
|  |         if (function.HasMetadata("DynamicOutputParam")) | |||
|  |         { | |||
|  |             var propertyDynamicOutputParam = function.Properties | |||
|  |                 .Where(p => p.EngineName == function.GetMetadata("DynamicOutputParam")) | |||
|  |                 .FirstOrDefault(); | |||
|  | 
 | |||
|  |             if (propertyDynamicOutputParam == null) return false; | |||
|  | 
 | |||
|  |             if (propertyDeterminingOutputType!.GetGenericManagedType() != propertyDynamicOutputParam.GetGenericManagedType()) return false; | |||
|  | 
 | |||
|  |             PropertyTranslator dopParamTranslator = PropertyTranslatorManager.GetTranslator(propertyDynamicOutputParam)!; | |||
|  |             return dopParamTranslator.CanSupportGenericType(propertyDynamicOutputParam); | |||
|  |         } | |||
|  |         else if (function.HasReturnProperty) | |||
|  |         { | |||
|  |             PropertyTranslator returnParamTranslator = PropertyTranslatorManager.GetTranslator(function.ReturnProperty!)!; | |||
|  |             return returnParamTranslator.CanSupportGenericType(function.ReturnProperty!); | |||
|  |         } | |||
|  | 
 | |||
|  |         return false; | |||
|  |     } | |||
|  | 
 | |||
|  |     public static string GetGenericTypeConstraint(this UhtFunction function) | |||
|  |     { | |||
|  |         if (!function.HasMetadata("DeterminesOutputType")) return string.Empty; | |||
|  | 
 | |||
|  |         var propertyDeterminingOutputType = function.Properties | |||
|  |             .Where(p => p.EngineName == function.GetMetadata("DeterminesOutputType")) | |||
|  |             .FirstOrDefault(); | |||
|  | 
 | |||
|  |         return propertyDeterminingOutputType?.GetGenericManagedType() ?? string.Empty; | |||
|  |     } | |||
|  | 
 | |||
|  |     public static bool HasCustomStructParamSupport(this UhtFunction function) | |||
|  |     { | |||
|  |         if (!function.HasMetadata("CustomStructureParam")) return false; | |||
|  | 
 | |||
|  |         var customStructParams = function.GetCustomStructParams(); | |||
|  |         return customStructParams.All(customParamName => | |||
|  |             function.Properties.Count(param => param.EngineName == customParamName) == 1); | |||
|  |     } | |||
|  | 
 | |||
|  |     public static List<string> GetCustomStructParams(this UhtFunction function) | |||
|  |     { | |||
|  |         if (!function.HasMetadata("CustomStructureParam")) return new List<string>(); | |||
|  | 
 | |||
|  |         return function.GetMetadata("CustomStructureParam").Split(",").ToList(); | |||
|  |     } | |||
|  |      | |||
|  |     public static int GetCustomStructParamCount(this UhtFunction function) => function.GetCustomStructParams().Count; | |||
|  |      | |||
|  |     public static List<string> GetCustomStructParamTypes(this UhtFunction function) | |||
|  |     { | |||
|  |         if (!function.HasMetadata("CustomStructureParam")) return new List<string>(); | |||
|  |         int paramCount = function.GetCustomStructParamCount(); | |||
|  |         if (paramCount == 1) return new List<string> { "CSP" }; | |||
|  |         return Enumerable.Range(0, paramCount).ToList().ConvertAll(i => $"CSP{i}"); | |||
|  |     } | |||
|  | 
 | |||
|  |     public static bool IsBlueprintNativeEvent(this UhtFunction function) | |||
|  |     { | |||
|  |         return function.HasAllFlags(EFunctionFlags.BlueprintEvent | EFunctionFlags.Native); | |||
|  |     } | |||
|  | 
 | |||
|  |     public static bool IsBlueprintImplementableEvent(this UhtFunction function) | |||
|  |     { | |||
|  |         return function.HasAllFlags(EFunctionFlags.BlueprintEvent) && !function.HasAllFlags(EFunctionFlags.Native); | |||
|  |     } | |||
|  | } |