@ -0,0 +1,428 @@
|
||||
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 class GetterSetterPair
|
||||
{
|
||||
public GetterSetterPair(UhtProperty property)
|
||||
{
|
||||
PropertyName = property.GetPropertyName();
|
||||
|
||||
if (!property.HasNativeGetter())
|
||||
{
|
||||
UhtFunction? foundGetter = property.GetBlueprintGetter();
|
||||
if (foundGetter != null)
|
||||
{
|
||||
Getter = foundGetter;
|
||||
GetterExporter = GetterSetterFunctionExporter.Create(foundGetter, property, GetterSetterMode.Get,
|
||||
EFunctionProtectionMode.UseUFunctionProtection);
|
||||
}
|
||||
}
|
||||
|
||||
if (!property.HasNativeSetter())
|
||||
{
|
||||
UhtFunction? foundSetter = property.GetBlueprintSetter();
|
||||
if (foundSetter != null)
|
||||
{
|
||||
Setter = foundSetter;
|
||||
SetterExporter = GetterSetterFunctionExporter.Create(foundSetter, property, GetterSetterMode.Set,
|
||||
EFunctionProtectionMode.UseUFunctionProtection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public GetterSetterPair(string propertyName)
|
||||
{
|
||||
PropertyName = propertyName;
|
||||
}
|
||||
|
||||
public readonly string PropertyName;
|
||||
|
||||
public UhtFunction? Getter { get; set; }
|
||||
public UhtFunction? Setter { get; set; }
|
||||
|
||||
public GetterSetterFunctionExporter? GetterExporter { get; set; }
|
||||
public GetterSetterFunctionExporter? SetterExporter { get; set; }
|
||||
|
||||
public List<UhtFunction> Accessors
|
||||
{
|
||||
get
|
||||
{
|
||||
List<UhtFunction> accessors = new();
|
||||
|
||||
UhtFunction? getter = Getter;
|
||||
if (getter != null)
|
||||
{
|
||||
accessors.Add(getter);
|
||||
}
|
||||
|
||||
UhtFunction? setter = Setter;
|
||||
if (setter != null)
|
||||
{
|
||||
accessors.Add(setter);
|
||||
}
|
||||
|
||||
return accessors;
|
||||
}
|
||||
}
|
||||
|
||||
public UhtProperty? Property { get; set; }
|
||||
}
|
||||
|
||||
public static class ScriptGeneratorUtilities
|
||||
{
|
||||
public const string InteropNamespace = "UnrealSharp.Interop";
|
||||
public const string MarshallerNamespace = "UnrealSharp.Core.Marshallers";
|
||||
public const string AttributeNamespace = "UnrealSharp.Attributes";
|
||||
public const string CoreAttributeNamespace = "UnrealSharp.Core.Attributes";
|
||||
public const string InteropServicesNamespace = "System.Runtime.InteropServices";
|
||||
|
||||
public const string PublicKeyword = "public ";
|
||||
public const string PrivateKeyword = "private ";
|
||||
public const string ProtectedKeyword = "protected ";
|
||||
|
||||
public const string IntPtrZero = "IntPtr.Zero";
|
||||
|
||||
public static string TryGetPluginDefine(string key)
|
||||
{
|
||||
Program.PluginModule.TryGetDefine(key, out string? generatedCodePath);
|
||||
return generatedCodePath!;
|
||||
}
|
||||
|
||||
public static bool CanExportFunction(UhtFunction function)
|
||||
{
|
||||
if (function.HasAnyFlags(EFunctionFlags.Delegate | EFunctionFlags.MulticastDelegate))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return CanExportParameters(function);
|
||||
}
|
||||
|
||||
public static bool CanExportParameters(UhtFunction function)
|
||||
{
|
||||
bool CanExportParameter(UhtProperty property, Func<PropertyTranslator, bool> isSupported)
|
||||
{
|
||||
PropertyTranslator? translator = PropertyTranslatorManager.GetTranslator(property);
|
||||
return translator != null && isSupported(translator) && translator.CanExport(property);
|
||||
}
|
||||
|
||||
if (function.ReturnProperty != null && !CanExportParameter(function.ReturnProperty,
|
||||
translator => translator.IsSupportedAsReturnValue()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (UhtProperty parameter in function.Properties)
|
||||
{
|
||||
if (!CanExportParameter(parameter, translator => translator.IsSupportedAsParameter()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool CanExportProperty(UhtProperty property)
|
||||
{
|
||||
PropertyTranslator? translator = PropertyTranslatorManager.GetTranslator(property);
|
||||
if (translator == null || !translator.CanExport(property))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isClassProperty = property.Outer!.EngineType == UhtEngineType.Class;
|
||||
bool canBeClassProperty = isClassProperty && translator.IsSupportedAsProperty();
|
||||
bool canBeStructProperty = !isClassProperty && translator.IsSupportedAsStructProperty();
|
||||
return canBeClassProperty || canBeStructProperty;
|
||||
}
|
||||
|
||||
public static string GetCleanEnumValueName(UhtEnum enumObj, UhtEnumValue enumValue)
|
||||
{
|
||||
if (enumObj.CppForm == UhtEnumCppForm.Regular)
|
||||
{
|
||||
return enumValue.Name;
|
||||
}
|
||||
|
||||
int delimiterIndex = enumValue.Name.IndexOf("::", StringComparison.Ordinal);
|
||||
return delimiterIndex < 0 ? enumValue.Name : enumValue.Name.Substring(delimiterIndex + 2);
|
||||
}
|
||||
|
||||
public static void GetExportedProperties(UhtStruct structObj, List<UhtProperty> properties,
|
||||
Dictionary<UhtProperty, GetterSetterPair> getterSetterBackedProperties)
|
||||
{
|
||||
if (!structObj.Properties.Any())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
UhtClass? classObj = structObj as UhtClass;
|
||||
foreach (UhtProperty property in structObj.Properties)
|
||||
{
|
||||
if (!CanExportProperty(property) || InclusionLists.HasBannedProperty(property))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (classObj != null && (property.HasAnyGetter() || property.HasAnySetter()))
|
||||
{
|
||||
GetterSetterPair pair = new GetterSetterPair(property);
|
||||
getterSetterBackedProperties.Add(property, pair);
|
||||
}
|
||||
else
|
||||
{
|
||||
properties.Add(property);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void GetExportedFunctions(UhtClass classObj, List<UhtFunction> functions,
|
||||
List<UhtFunction> overridableFunctions,
|
||||
Dictionary<string, GetterSetterPair> getterSetterPairs,
|
||||
Dictionary<string, GetterSetterPair> getSetOverrides)
|
||||
{
|
||||
List<UhtFunction> exportedFunctions = new();
|
||||
|
||||
bool HasFunction(List<UhtFunction> functionsToCheck, UhtFunction functionToTest)
|
||||
{
|
||||
foreach (UhtFunction function in functionsToCheck)
|
||||
{
|
||||
if (function.SourceName == functionToTest.SourceName ||
|
||||
function.CppImplName == functionToTest.CppImplName)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (UhtFunction function in classObj.Functions)
|
||||
{
|
||||
if (!CanExportFunction(function))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (function.IsAnyGetter() || function.IsAnySetter())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (function.FunctionFlags.HasAnyFlags(EFunctionFlags.BlueprintEvent))
|
||||
{
|
||||
overridableFunctions.Add(function);
|
||||
}
|
||||
else if (function.IsAutocast())
|
||||
{
|
||||
functions.Add(function);
|
||||
|
||||
if (function.Properties.First() is not UhtStructProperty structToConvertProperty)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (structToConvertProperty.Package.IsPartOfEngine() != function.Package.IsPartOfEngine())
|
||||
{
|
||||
// For auto-casts to work, they both need to be in the same generated assembly.
|
||||
// Currently not supported, as we separate engine and project generated assemblies.
|
||||
continue;
|
||||
}
|
||||
|
||||
AutocastExporter.AddAutocastFunction(structToConvertProperty.ScriptStruct, function);
|
||||
}
|
||||
else if (!TryMakeFunctionGetterSetterPair(function, classObj, getterSetterPairs, false))
|
||||
{
|
||||
functions.Add(function);
|
||||
}
|
||||
|
||||
exportedFunctions.Add(function);
|
||||
}
|
||||
|
||||
foreach (UhtClass declaration in classObj.GetInterfaces())
|
||||
{
|
||||
UhtClass? interfaceClass = declaration.GetInterfaceAlternateClass();
|
||||
|
||||
if (interfaceClass == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (UhtFunction function in interfaceClass.Functions)
|
||||
{
|
||||
if (TryMakeFunctionGetterSetterPair(function, interfaceClass, getSetOverrides, true)
|
||||
|| HasFunction(exportedFunctions, function) || !CanExportFunction(function))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (function.FunctionFlags.HasAnyFlags(EFunctionFlags.BlueprintEvent))
|
||||
{
|
||||
overridableFunctions.Add(function);
|
||||
}
|
||||
else
|
||||
{
|
||||
functions.Add(function);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static List<UhtClass> GetInterfaces(this UhtClass classObj)
|
||||
{
|
||||
List<UhtClass> interfaces = new();
|
||||
foreach (UhtStruct interfaceClass in classObj.Bases)
|
||||
{
|
||||
UhtEngineType engineType = interfaceClass.EngineType;
|
||||
if (engineType is UhtEngineType.Interface or UhtEngineType.NativeInterface)
|
||||
{
|
||||
interfaces.Add((UhtClass)interfaceClass);
|
||||
}
|
||||
}
|
||||
|
||||
return interfaces;
|
||||
}
|
||||
|
||||
public static bool TryMakeFunctionGetterSetterPair(UhtFunction function, UhtClass classObj,
|
||||
Dictionary<string, GetterSetterPair> getterSetterPairs, bool ignoreMatchingProperty)
|
||||
{
|
||||
string scriptName = function.GetFunctionName();
|
||||
bool isGetter = CheckIfGetter(scriptName, function);
|
||||
bool isSetter = CheckIfSetter(scriptName, function);
|
||||
|
||||
if (!isGetter && !isSetter)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
string propertyName = scriptName.Length > 3 ? scriptName.Substring(3) : function.SourceName;
|
||||
propertyName = NameMapper.EscapeKeywords(propertyName);
|
||||
|
||||
UhtFunction? sameNameFunction = classObj.FindFunctionByName(propertyName);
|
||||
|
||||
if (sameNameFunction != null && sameNameFunction != function)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ComparePropertyName(UhtProperty prop, string name)
|
||||
{
|
||||
return prop.SourceName == name || prop.GetPropertyName() == name;
|
||||
}
|
||||
|
||||
UhtProperty? classProperty = !ignoreMatchingProperty ? classObj.FindPropertyByName(propertyName, ComparePropertyName) : null;
|
||||
UhtProperty firstProperty = function.ReturnProperty ?? function.Properties.First();
|
||||
|
||||
if (classProperty != null && (!classProperty.IsSameType(firstProperty) || classProperty.HasAnyGetter() ||
|
||||
classProperty.HasAnySetter()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!getterSetterPairs.TryGetValue(propertyName, out GetterSetterPair? pair))
|
||||
{
|
||||
pair = new GetterSetterPair(propertyName);
|
||||
getterSetterPairs[propertyName] = pair;
|
||||
}
|
||||
|
||||
if (pair.Accessors.Count == 2)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isOutParm = function.Properties.Any(p =>
|
||||
p.HasAllFlags(EPropertyFlags.OutParm) && !p.HasAllFlags(EPropertyFlags.ConstParm));
|
||||
|
||||
if (function.ReturnProperty != null || isOutParm)
|
||||
{
|
||||
pair.Getter = function;
|
||||
// When creating the getter, bind it to the getter's own value type (return or out param)
|
||||
UhtProperty getterValueProperty = function.ReturnProperty ?? function.Properties.First(p =>
|
||||
p.HasAllFlags(EPropertyFlags.OutParm) && !p.HasAllFlags(EPropertyFlags.ConstParm));
|
||||
pair.GetterExporter = GetterSetterFunctionExporter.Create(function, getterValueProperty, GetterSetterMode.Get,
|
||||
EFunctionProtectionMode.UseUFunctionProtection);
|
||||
|
||||
UhtFunction? setter = classObj.FindFunctionByName("Set" + propertyName, null, true);
|
||||
if (setter != null && CheckIfSetter(setter))
|
||||
{
|
||||
pair.Setter = setter;
|
||||
// Keep using the getter's value type as the canonical property type
|
||||
pair.SetterExporter = GetterSetterFunctionExporter.Create(setter, getterValueProperty, GetterSetterMode.Set,
|
||||
EFunctionProtectionMode.UseUFunctionProtection);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pair.Setter = function;
|
||||
pair.SetterExporter = GetterSetterFunctionExporter.Create(function, firstProperty, GetterSetterMode.Set,
|
||||
EFunctionProtectionMode.UseUFunctionProtection);
|
||||
|
||||
UhtFunction? getter = classObj.FindFunctionByName("Get" + propertyName, null, true);
|
||||
if (getter != null && CheckIfGetter(getter))
|
||||
{
|
||||
pair.Getter = getter;
|
||||
// Prefer the getter's own value type (return or out param) for the property type
|
||||
UhtProperty getterValueProperty = getter.ReturnProperty ?? getter.Properties.First(p =>
|
||||
p.HasAllFlags(EPropertyFlags.OutParm) && !p.HasAllFlags(EPropertyFlags.ConstParm));
|
||||
pair.GetterExporter = GetterSetterFunctionExporter.Create(getter, getterValueProperty, GetterSetterMode.Get,
|
||||
EFunctionProtectionMode.UseUFunctionProtection);
|
||||
// Also re-bind the setter exporter to the getter's value type so signatures align
|
||||
pair.SetterExporter = GetterSetterFunctionExporter.Create(function, getterValueProperty, GetterSetterMode.Set,
|
||||
EFunctionProtectionMode.UseUFunctionProtection);
|
||||
}
|
||||
}
|
||||
|
||||
// Canonical property type: prefer getter value type when available, else fall back to current function's type
|
||||
if (pair.Getter != null)
|
||||
{
|
||||
pair.Property = pair.Getter.ReturnProperty ?? pair.Getter.Properties.First(p =>
|
||||
p.HasAllFlags(EPropertyFlags.OutParm) && !p.HasAllFlags(EPropertyFlags.ConstParm));
|
||||
}
|
||||
else
|
||||
{
|
||||
pair.Property = firstProperty;
|
||||
}
|
||||
getterSetterPairs[propertyName] = pair;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool CheckIfGetter(string scriptName, UhtFunction function)
|
||||
{
|
||||
return scriptName.StartsWith("Get") && CheckIfGetter(function);
|
||||
}
|
||||
|
||||
static bool CheckIfGetter(UhtFunction function)
|
||||
{
|
||||
int childrenCount = function.Children.Count;
|
||||
bool hasReturnProperty = function.ReturnProperty != null;
|
||||
bool hasNoParameters = !function.HasParameters;
|
||||
bool hasSingleOutParam = !hasNoParameters && childrenCount == 1 && function.HasOutParams();
|
||||
bool hasWorldContextPassParam =
|
||||
childrenCount == 2 && function.Properties.Any(property => property.IsWorldContextParameter());
|
||||
bool isNotBlueprintEvent = !function.FunctionFlags.HasAnyFlags(EFunctionFlags.BlueprintEvent);
|
||||
return hasReturnProperty && isNotBlueprintEvent && (hasNoParameters || hasSingleOutParam || hasWorldContextPassParam);
|
||||
}
|
||||
|
||||
static bool CheckIfSetter(string scriptName, UhtFunction function)
|
||||
{
|
||||
return scriptName.StartsWith("Set") && CheckIfSetter(function);
|
||||
}
|
||||
|
||||
static bool CheckIfSetter(UhtFunction function)
|
||||
{
|
||||
bool hasSingleParameter = function.Properties.Count() == 1;
|
||||
var property = function.Properties.FirstOrDefault();
|
||||
bool isNotOutOrReferenceParam = function.HasParameters && property is not null
|
||||
&& (!property.HasAllFlags(EPropertyFlags.OutParm | EPropertyFlags.ReferenceParm)
|
||||
|| property.HasAllFlags(EPropertyFlags.ConstParm));
|
||||
bool isNotBlueprintEvent = !function.FunctionFlags.HasAnyFlags(EFunctionFlags.BlueprintEvent);
|
||||
return hasSingleParameter && isNotBlueprintEvent && isNotOutOrReferenceParam;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user