Files
BusyRabbit/Plugins/UnrealSharp/Source/UnrealSharpManagedGlue/Exporters/FunctionExporter.cs

1270 lines
49 KiB
C#
Raw Normal View History

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data.Common;
using System.Linq;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using EpicGames.Core;
using EpicGames.UHT.Types;
using UnrealSharpScriptGenerator.PropertyTranslators;
using UnrealSharpScriptGenerator.Tooltip;
using UnrealSharpScriptGenerator.Utilities;
namespace UnrealSharpScriptGenerator.Exporters;
public enum EFunctionProtectionMode
{
UseUFunctionProtection,
OverrideWithInternal,
}
public struct ExtensionMethod
{
public UhtClass Class;
public UhtFunction Function;
public UhtProperty SelfParameter;
}
public enum FunctionType
{
Normal,
BlueprintEvent,
ExtensionOnAnotherClass,
InternalWhitelisted,
GetterSetter,
};
public enum OverloadMode
{
AllowOverloads,
SuppressOverloads,
};
public enum EBlueprintVisibility
{
Call,
Event,
GetterSetter,
Throwing
};
public struct FunctionOverload
{
public string ParamStringApiWithDefaults;
public string ParamsStringCall;
public string CSharpParamName;
public string CppDefaultValue;
public PropertyTranslator Translator;
public UhtProperty Parameter;
}
public class FunctionExporter
{
protected static readonly ConcurrentDictionary<UhtPackage, List<ExtensionMethod>> ExtensionMethods = new();
public UhtFunction Function { get; }
protected string _functionName = null!;
protected List<PropertyTranslator> _parameterTranslators = null!;
protected PropertyTranslator? ReturnValueTranslator => Function.ReturnProperty != null ? _parameterTranslators.Last() : null;
protected OverloadMode _overloadMode = OverloadMode.AllowOverloads;
protected EFunctionProtectionMode _protectionMode = EFunctionProtectionMode.UseUFunctionProtection;
protected EBlueprintVisibility _blueprintVisibility = EBlueprintVisibility.Call;
protected bool BlittableFunction;
public string Modifiers { get; private set; } = "";
protected bool BlueprintEvent => Function.HasAllFlags(EFunctionFlags.BlueprintEvent);
protected bool BlueprintNativeEvent => Function.IsBlueprintNativeEvent();
protected bool BlueprintImplementableEvent => Function.IsBlueprintImplementableEvent();
protected bool Throwing => _blueprintVisibility == EBlueprintVisibility.Throwing;
protected string _invokeFunction = "";
protected string _invokeFirstArgument = "";
protected string _customInvoke = "";
protected string _paramStringApiWithDefaults = "";
protected string _paramsStringCall = "";
protected bool _hasGenericTypeSupport = false;
protected bool _hasCustomStructParamSupport = false;
protected List<string> _customStructParamTypes = null!;
protected readonly UhtProperty? _selfParameter;
protected readonly UhtClass? _classBeingExtended;
protected readonly List<FunctionOverload> _overloads = new();
public string NativeFunctionIntPtr => $"{Function.SourceName}_NativeFunction";
public string InstanceFunctionPtr => $"{Function.SourceName}_InstanceFunction";
public FunctionExporter(ExtensionMethod extensionMethod)
{
_selfParameter = extensionMethod.SelfParameter;
Function = extensionMethod.Function;
_classBeingExtended = extensionMethod.Class;
}
public FunctionExporter(UhtFunction function)
{
Function = function;
}
string GetRefQualifier(UhtProperty parameter)
{
if (parameter.HasAllFlags(EPropertyFlags.ConstParm))
{
return "";
}
if (parameter.HasAllFlags(EPropertyFlags.ReferenceParm))
{
return "ref ";
}
if (parameter.HasAllFlags(EPropertyFlags.OutParm))
{
return "out ";
}
return "";
}
public void Initialize(OverloadMode overloadMode, EFunctionProtectionMode protectionMode, EBlueprintVisibility blueprintVisibility, bool withGenerics = false)
{
_functionName = protectionMode != EFunctionProtectionMode.OverrideWithInternal
? Function.GetFunctionName()
: Function.SourceName;
_overloadMode = overloadMode;
_protectionMode = protectionMode;
_blueprintVisibility = blueprintVisibility;
_parameterTranslators = new List<PropertyTranslator>(Function.Children.Count);
bool isBlittable = true;
foreach (UhtProperty parameter in Function.Properties)
{
PropertyTranslator translator = PropertyTranslatorManager.GetTranslator(parameter)!;
_parameterTranslators.Add(translator);
if (!translator.IsBlittable && isBlittable)
{
isBlittable = false;
}
}
BlittableFunction = isBlittable;
_hasGenericTypeSupport = Function.HasGenericTypeSupport();
_hasCustomStructParamSupport = Function.HasCustomStructParamSupport();
DetermineProtectionMode();
_invokeFunction = DetermineInvokeFunction();
if (Function.HasAllFlags(EFunctionFlags.Static))
{
Modifiers += "static ";
_invokeFirstArgument = "NativeClassPtr";
}
else if (Function.HasAllFlags(EFunctionFlags.Delegate))
{
if (Function.HasParametersOrReturnValue())
{
_customInvoke = "ProcessDelegate(paramsBuffer);";
}
else
{
_customInvoke = "ProcessDelegate(IntPtr.Zero);";
}
}
else
{
if (BlueprintEvent)
{
Modifiers += "virtual ";
}
if (Function.IsInterfaceFunction())
{
Modifiers = ScriptGeneratorUtilities.PublicKeyword;
}
_invokeFirstArgument = "NativeObject";
}
string paramString = "";
bool hasDefaultParameters = false;
if (_selfParameter != null)
{
PropertyTranslator translator = PropertyTranslatorManager.GetTranslator(_selfParameter)!;
string paramType = _classBeingExtended != null
? _classBeingExtended.GetFullManagedName()
: translator.GetManagedType(_selfParameter);
paramString = $"this {paramType} {_selfParameter.GetParameterName()}, ";
_paramStringApiWithDefaults = paramString;
}
string paramsStringCallNative = "";
string paramsStringCallGenerics = "";
string paramStringApiWithDefaultsWithGenerics = "";
bool hasGenericClassParam = false;
_customStructParamTypes = Function.GetCustomStructParamTypes();
for (int i = 0; i < Function.Children.Count; i++)
{
UhtProperty parameter = (UhtProperty) Function.Children[i];
if (parameter.HasAllFlags(EPropertyFlags.ReturnParm))
{
continue;
}
PropertyTranslator translator = _parameterTranslators[i];
string refQualifier = GetRefQualifier(parameter);
string parameterName = GetParameterName(parameter);
string parameterManagedType = translator.GetManagedType(parameter);
if (!translator.ShouldBeDeclaredAsParameter)
{
continue;
}
if (_selfParameter == parameter)
{
if (string.IsNullOrEmpty(paramsStringCallGenerics))
{
paramsStringCallGenerics += refQualifier + parameterName;
}
else
{
paramsStringCallGenerics = $"{parameterName}, " + _paramsStringCall.Substring(0, _paramsStringCall.Length - 2);
}
if (string.IsNullOrEmpty(_paramsStringCall))
{
_paramsStringCall += refQualifier + parameterName;
}
else
{
_paramsStringCall = $"{parameterName}, " + _paramsStringCall.Substring(0, _paramsStringCall.Length - 2);
}
paramsStringCallNative += parameterName;
}
else
{
string cppDefaultValue = translator.GetCppDefaultValue(Function, parameter);
bool isGenericClassParam = _hasGenericTypeSupport && parameter.IsGenericType() && !parameter.HasAnyFlags(EPropertyFlags.OutParm) && parameter is UhtClassProperty;
if (cppDefaultValue == "()" && parameter is UhtStructProperty structProperty)
{
_paramsStringCall += $"new {structProperty.ScriptStruct.GetFullManagedName()}()";
paramsStringCallGenerics += $"new {structProperty.ScriptStruct.GetFullManagedName()}()";
}
else if (isGenericClassParam)
{
_paramsStringCall += $"{refQualifier}{parameterName}";
paramsStringCallGenerics += $"typeof(DOT)";
hasGenericClassParam = true;
}
else
{
_paramsStringCall += $"{refQualifier}{parameterName}";
paramsStringCallGenerics += $"{refQualifier}{parameterName}";
}
paramsStringCallNative += $"{refQualifier}{parameterName}";
paramString += $"{refQualifier}{parameterManagedType} {parameterName}";
if (!isGenericClassParam)
{
paramStringApiWithDefaultsWithGenerics += $"{refQualifier}{parameterManagedType} {parameterName}";
}
if ((hasDefaultParameters || cppDefaultValue.Length > 0) && _overloadMode == OverloadMode.AllowOverloads)
{
hasDefaultParameters = true;
string csharpDefaultValue = "";
if (cppDefaultValue.Length == 0 || cppDefaultValue == "None")
{
csharpDefaultValue = translator.GetNullValue(parameter);
}
else if (translator.ExportDefaultParameter)
{
csharpDefaultValue = translator.ConvertCPPDefaultValue(cppDefaultValue, Function, parameter);
}
if (!string.IsNullOrEmpty(csharpDefaultValue))
{
string defaultValue = $" = {csharpDefaultValue}";
_paramStringApiWithDefaults += $"{refQualifier}{parameterManagedType} {parameterName}{defaultValue}";
}
else
{
if (_paramStringApiWithDefaults.Length > 0)
{
_paramStringApiWithDefaults = _paramStringApiWithDefaults.Substring(0, _paramStringApiWithDefaults.Length - 2);
}
FunctionOverload overload = new FunctionOverload
{
ParamStringApiWithDefaults = _paramStringApiWithDefaults,
ParamsStringCall = _paramsStringCall,
CSharpParamName = parameterName,
CppDefaultValue = cppDefaultValue,
Translator = translator,
Parameter = parameter,
};
_overloads.Add(overload);
_paramStringApiWithDefaults = paramString;
}
}
else
{
_paramStringApiWithDefaults = paramString;
}
paramString += ", ";
_paramStringApiWithDefaults += ", ";
if (!isGenericClassParam)
{
paramStringApiWithDefaultsWithGenerics += ", ";
}
}
_paramsStringCall += ", ";
paramsStringCallGenerics += ", ";
paramsStringCallNative += ", ";
}
if (_selfParameter == null)
{
_paramsStringCall = paramsStringCallNative;
}
// remove last comma
if (_paramStringApiWithDefaults.Length > 0)
{
_paramStringApiWithDefaults = _paramStringApiWithDefaults.Substring(0, _paramStringApiWithDefaults.Length - 2);
}
if (_paramsStringCall.Length > 0)
{
_paramsStringCall = _paramsStringCall.Substring(0, _paramsStringCall.Length - 2);
}
if (paramsStringCallGenerics.Length > 0)
{
paramsStringCallGenerics = paramsStringCallGenerics[..^2];
}
if (paramStringApiWithDefaultsWithGenerics.Length > 0)
{
paramStringApiWithDefaultsWithGenerics = paramStringApiWithDefaultsWithGenerics[..^2];
}
if (hasGenericClassParam)
{
FunctionOverload overload = new FunctionOverload
{
ParamStringApiWithDefaults = paramStringApiWithDefaultsWithGenerics,
ParamsStringCall = paramsStringCallGenerics,
};
_overloads.Add(overload);
}
}
protected virtual string GetParameterName(UhtProperty parameter)
{
return parameter.GetParameterName();
}
public static void TryAddExtensionMethod(UhtFunction function)
{
if (!function.HasMetadata("ExtensionMethod") && !function.IsAutocast())
{
return;
}
UhtPackage package = function.Outer!.Package;
if (!ExtensionMethods.TryGetValue(package, out var extensionMethods))
{
extensionMethods = new List<ExtensionMethod>();
ExtensionMethods.TryAdd(package, extensionMethods);
}
UhtProperty firstParameter = (function.Children[0] as UhtProperty)!;
ExtensionMethod newExtensionMethod = new ExtensionMethod
{
Function = function,
SelfParameter = firstParameter,
};
if (firstParameter is UhtObjectPropertyBase objectSelfProperty)
{
newExtensionMethod.Class = objectSelfProperty.MetaClass!;
}
extensionMethods.Add(newExtensionMethod);
}
public static void StartExportingExtensionMethods(List<Task> tasks)
{
foreach (KeyValuePair<UhtPackage, List<ExtensionMethod>> extensionInfo in ExtensionMethods)
{
tasks.Add(Program.Factory.CreateTask(_ =>
{
ExtensionsClassExporter.ExportExtensionsClass(extensionInfo.Key, extensionInfo.Value);
})!);
}
}
public static FunctionExporter ExportFunction(GeneratorStringBuilder builder, UhtFunction function, FunctionType functionType, HashSet<string>? exportedFunctions = null)
{
EFunctionProtectionMode protectionMode = EFunctionProtectionMode.UseUFunctionProtection;
OverloadMode overloadMode = OverloadMode.AllowOverloads;
EBlueprintVisibility blueprintVisibility = EBlueprintVisibility.Call;
if (functionType == FunctionType.ExtensionOnAnotherClass)
{
protectionMode = EFunctionProtectionMode.OverrideWithInternal;
overloadMode = OverloadMode.SuppressOverloads;
}
else if (functionType == FunctionType.BlueprintEvent)
{
overloadMode = OverloadMode.SuppressOverloads;
blueprintVisibility = EBlueprintVisibility.Event;
}
else if (functionType == FunctionType.GetterSetter)
{
protectionMode = EFunctionProtectionMode.OverrideWithInternal;
overloadMode = OverloadMode.SuppressOverloads;
blueprintVisibility = EBlueprintVisibility.GetterSetter;
}
builder.TryAddWithEditor(function);
FunctionExporter exporter = new FunctionExporter(function);
exporter.Initialize(overloadMode, protectionMode, blueprintVisibility);
if (exportedFunctions is null || !exportedFunctions.Contains(function.SourceName))
{
exporter.ExportFunctionVariables(builder);
}
exporter.ExportOverloads(builder);
exporter.ExportFunction(builder);
builder.TryEndWithEditor(function);
return exporter;
}
public static void ExportOverridableFunction(GeneratorStringBuilder builder, UhtFunction function, HashSet<string>? exportedFunctions = null)
{
builder.TryAddWithEditor(function);
string paramsStringApi = "";
string paramsCallString = "";
string methodName = function.GetFunctionName();
foreach (UhtProperty parameter in function.Properties)
{
if (parameter.HasAllFlags(EPropertyFlags.ReturnParm))
{
continue;
}
string paramName = parameter.GetParameterName();
string paramType = PropertyTranslatorManager.GetTranslator(parameter)!.GetManagedType(parameter);
string refQualifier = "";
if (!parameter.HasAllFlags(EPropertyFlags.ConstParm))
{
if (parameter.HasAllFlags(EPropertyFlags.ReferenceParm))
{
refQualifier = "ref ";
}
else if (parameter.HasAllFlags(EPropertyFlags.OutParm))
{
refQualifier = "out ";
}
}
paramsStringApi += $"{refQualifier}{paramType} {paramName}, ";
paramsCallString += $"{refQualifier}{paramName}, ";
}
if (paramsStringApi.Length > 0)
{
paramsStringApi = paramsStringApi.Substring(0, paramsStringApi.Length - 2);
}
if (paramsCallString.Length > 0)
{
paramsCallString = paramsCallString.Substring(0, paramsCallString.Length - 2);
}
FunctionExporter exportFunction = ExportFunction(builder, function, FunctionType.BlueprintEvent, exportedFunctions);
string returnType = function.ReturnProperty != null
? PropertyTranslatorManager.GetTranslator(function.ReturnProperty)!.GetManagedType(function.ReturnProperty)
: "void";
builder.AppendLine("// Hide implementation function from Intellisense");
builder.AppendLine("[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]");
builder.AppendLine($"protected virtual {returnType} {methodName}_Implementation({paramsStringApi})");
builder.OpenBrace();
if (exportFunction.BlueprintNativeEvent)
{
exportFunction.ExportInvoke(builder);
}
else
{
exportFunction.ForEachParameter((translator, parameter) =>
{
if (!parameter.HasAllFlags(EPropertyFlags.OutParm) || parameter.HasAnyFlags(EPropertyFlags.ReturnParm | EPropertyFlags.ConstParm | EPropertyFlags.ReferenceParm))
{
return;
}
string paramName = parameter.GetParameterName();
string nullValue = translator.GetNullValue(parameter);
builder.AppendLine($"{paramName} = {nullValue};");
});
if (function.ReturnProperty != null)
{
PropertyTranslator translator = PropertyTranslatorManager.GetTranslator(function.ReturnProperty)!;
string nullValue = translator.GetNullValue(function.ReturnProperty);
builder.AppendLine($"return {nullValue};");
}
}
builder.CloseBrace();
builder.AppendLine($"void Invoke_{function.EngineName}(IntPtr buffer, IntPtr returnBuffer)");
builder.OpenBrace();
builder.BeginUnsafeBlock();
string returnAssignment = "";
exportFunction.ForEachParameter((translator, parameter) =>
{
string paramType = translator.GetManagedType(parameter);
if (parameter.HasAllFlags(EPropertyFlags.ReturnParm))
{
returnAssignment = $"{paramType} returnValue = ";
}
else if (!parameter.HasAnyFlags(EPropertyFlags.ConstParm)
&& !parameter.HasAnyFlags(EPropertyFlags.ReferenceParm)
&& parameter.HasAnyFlags(EPropertyFlags.OutParm))
{
builder.AppendLine($"{paramType} {parameter.GetParameterName()} = default;");
}
else
{
string parameterName = parameter.GetParameterName();
string assignmentOrReturn = $"{paramType} {parameterName} = ";
string offsetName = parameter.GetOffsetVariableName();
translator.ExportFromNative(builder, parameter, parameter.SourceName, assignmentOrReturn, "buffer",
offsetName, false, false);
}
});
builder.AppendLine($"{returnAssignment}{methodName}_Implementation({paramsCallString});");
if (function.ReturnProperty != null)
{
PropertyTranslator translator = PropertyTranslatorManager.GetTranslator(function.ReturnProperty)!;
translator.ExportToNative(builder, function.ReturnProperty, function.ReturnProperty.SourceName, "returnBuffer", "0", "returnValue");
}
exportFunction.ForEachParameter((translator, parameter) =>
{
if (parameter.HasAnyFlags(EPropertyFlags.ReturnParm | EPropertyFlags.ConstParm) ||
!parameter.HasAnyFlags(EPropertyFlags.OutParm))
{
return;
}
translator.ExportToNative(builder, parameter, parameter.SourceName, "buffer", parameter.GetOffsetVariableName(), parameter.GetParameterName());
});
builder.EndUnsafeBlock();
builder.CloseBrace();
builder.TryEndWithEditor(function);
builder.AppendLine();
}
public static FunctionExporter ExportDelegateSignature(GeneratorStringBuilder builder, UhtFunction function, string delegateName)
{
FunctionExporter exporter = new FunctionExporter(function);
exporter.Initialize(OverloadMode.SuppressOverloads, EFunctionProtectionMode.UseUFunctionProtection, EBlueprintVisibility.Call);
AttributeBuilder attributeBuilder = new AttributeBuilder();
attributeBuilder.AddGeneratedTypeAttribute(function);
if (function.HasAllFlags(EFunctionFlags.MulticastDelegate))
{
attributeBuilder.AddAttribute("UMultiDelegate");
}
else
{
attributeBuilder.AddAttribute("USingleDelegate");
}
attributeBuilder.Finish();
builder.AppendLine(attributeBuilder.ToString());
builder.AppendLine($"public delegate void {delegateName}({exporter._paramStringApiWithDefaults});");
builder.AppendLine();
return exporter;
}
public static void ExportDelegateGlue(GeneratorStringBuilder builder, FunctionExporter exporter)
{
exporter.ExportFunctionVariables(builder);
builder.AppendLine();
builder.AppendLine($"protected void Invoker({exporter._paramStringApiWithDefaults})");
builder.OpenBrace();
exporter.ExportInvoke(builder);
builder.CloseBrace();
}
public static void ExportInterfaceFunction(GeneratorStringBuilder builder, UhtFunction function)
{
builder.TryAddWithEditor(function);
FunctionExporter exporter = new FunctionExporter(function);
exporter.Initialize(OverloadMode.SuppressOverloads, EFunctionProtectionMode.UseUFunctionProtection, EBlueprintVisibility.Call);
exporter.ExportSignature(builder, ScriptGeneratorUtilities.PublicKeyword);
builder.Append(";");
builder.TryEndWithEditor(function);
}
public void ForEachParameter(Action<PropertyTranslator, UhtProperty> action)
{
for (int i = 0; i < Function.Children.Count; i++)
{
UhtProperty parameter = (UhtProperty) Function.Children[i];
PropertyTranslator translator = _parameterTranslators[i];
action(translator, parameter);
}
}
public void ExportExtensionMethodOverloads(GeneratorStringBuilder builder)
{
foreach (FunctionOverload overload in _overloads)
{
builder.AppendLine();
ExportDeprecation(builder);
string returnType = "void";
string returnStatement = "";
if (Function.ReturnProperty != null)
{
returnType = ReturnValueTranslator!.GetManagedType(Function.ReturnProperty);
returnStatement = "return ";
}
List<string> genericTypes = new List<string>();
List<string> genericConstraints = new List<string>();
if (_hasGenericTypeSupport)
{
genericTypes.Add("DOT");
genericConstraints.Add(Function.GetGenericTypeConstraint());
}
if (_hasCustomStructParamSupport)
{
genericTypes.AddRange(_customStructParamTypes);
genericConstraints.AddRange(_customStructParamTypes.ConvertAll(paramType => $"MarshalledStruct<{paramType}>"));
}
string genericTypeString = string.Join(", ", genericTypes);
if (genericTypes.Count > 0)
{
PropertyTranslator translator = _parameterTranslators[0];
string paramType = _classBeingExtended != null
? _classBeingExtended.GetFullManagedName()
: translator.GetManagedType(_selfParameter!);
builder.AppendLine($"{Modifiers}{returnType} {_functionName}<{genericTypeString}>(this {paramType} {_selfParameter!.GetParameterName()}, {overload.ParamStringApiWithDefaults})");
builder.Indent();
foreach (var (genericType, constraint) in genericTypes.Zip(genericConstraints))
builder.AppendLine($"where {genericType} : {constraint}");
builder.UnIndent();
}
else
{
builder.AppendLine($"{Modifiers}{returnType} {_functionName}(this {overload.ParamStringApiWithDefaults})");
}
builder.OpenBrace();
overload.Translator?.ExportCppDefaultParameterAsLocalVariable(builder, overload.CSharpParamName, overload.CppDefaultValue, Function, overload.Parameter);
UhtClass functionOwner = (UhtClass) Function.Outer!;
string fullClassName = functionOwner.GetFullManagedName();
if (genericTypes.Count > 0)
{
builder.AppendLine($"{returnStatement}{fullClassName}.{_functionName}<{genericTypeString}>({overload.ParamsStringCall});");
}
else
{
builder.AppendLine($"{returnStatement}{fullClassName}.{_functionName}({overload.ParamsStringCall});");
}
builder.CloseBrace();
}
}
public void ExportExtensionMethod(GeneratorStringBuilder builder)
{
builder.AppendLine();
builder.AppendTooltip(Function);
ExportDeprecation(builder);
string returnManagedType = "void";
if (ReturnValueTranslator != null)
{
returnManagedType = ReturnValueTranslator.GetManagedType(Function.ReturnProperty!);
}
string functionNameToUse = Function.IsAutocast() ? Function.GetBlueprintAutocastName() : _functionName;
List<string> genericTypes = new List<string>();
List<string> genericConstraints = new List<string>();
if (_hasGenericTypeSupport)
{
genericTypes.Add("DOT");
genericConstraints.Add(Function.GetGenericTypeConstraint());
}
if (_hasCustomStructParamSupport)
{
genericTypes.AddRange(_customStructParamTypes);
genericConstraints.AddRange(_customStructParamTypes.ConvertAll(paramType => $"MarshalledStruct<{paramType}>"));
}
string genericTypeString = string.Join(", ", genericTypes);
if (genericTypes.Count > 0)
{
builder.AppendLine($"{Modifiers}{returnManagedType} {functionNameToUse}<{genericTypeString}>({_paramStringApiWithDefaults})");
builder.Indent();
foreach (var (genericType, constraint) in genericTypes.Zip(genericConstraints))
builder.AppendLine($"where {genericType} : {constraint}");
builder.UnIndent();
}
else
{
builder.AppendLine($"{Modifiers}{returnManagedType} {functionNameToUse}({_paramStringApiWithDefaults})");
}
builder.OpenBrace();
string returnStatement = Function.ReturnProperty != null ? "return " : "";
UhtClass functionOwner = (UhtClass) Function.Outer!;
string fullClassName = functionOwner.GetFullManagedName();
builder.AppendLine($"{returnStatement}{fullClassName}.{_functionName}({_paramsStringCall});");
builder.CloseBrace();
}
public void ExportFunctionVariables(GeneratorStringBuilder builder)
{
builder.AppendLine($"// {Function.SourceName}");
if (!BlueprintImplementableEvent)
{
builder.AppendLine($"static IntPtr {NativeFunctionIntPtr};");
}
if (BlueprintEvent)
{
builder.AppendLine($"IntPtr {InstanceFunctionPtr};");
}
if (Function.HasParametersOrReturnValue())
{
if (_hasCustomStructParamSupport)
{
string genericTypes = string.Join(", ", _customStructParamTypes);
builder.AppendLine($"static int {Function.SourceName}_NativeParamsSize;");
builder.AppendLine($"static int {Function.SourceName}_ParamsSize<{genericTypes}>()");
builder.Indent();
foreach (string genericType in _customStructParamTypes)
{
builder.AppendLine($"where {genericType}: MarshalledStruct<{genericType}>");
}
List<string> variableNames = new List<string>{$"{Function.SourceName}_NativeParamsSize"};
int customStructureParamIndex = 0;
ForEachParameter((translator, parameter) =>
{
if (!parameter.IsCustomStructureType()) return;
variableNames.Add($"{_customStructParamTypes[customStructureParamIndex]}.GetNativeDataSize()");
customStructureParamIndex++;
});
builder.AppendLine($"=> {string.Join(" + ", variableNames)};");
builder.UnIndent();
builder.AppendLine($"static IntPtr[] {Function.SourceName}_CustomStructureNativeProperties;");
}
else
{
builder.AppendLine($"static int {Function.SourceName}_ParamsSize;");
}
}
ForEachParameter((translator, parameter) =>
{
translator.ExportParameterVariables(builder, Function, Function.SourceName, parameter, parameter.SourceName);
});
}
void ExportOverloads(GeneratorStringBuilder builder)
{
foreach (FunctionOverload overload in _overloads)
{
builder.AppendLine();
ExportDeprecation(builder);
string returnType = "void";
string returnStatement = "";
if (Function.ReturnProperty != null)
{
returnType = ReturnValueTranslator!.GetManagedType(Function.ReturnProperty);
returnStatement = "return ";
}
List<string> genericTypes = new List<string>();
List<string> genericConstraints = new List<string>();
if (_hasGenericTypeSupport)
{
genericTypes.Add("DOT");
genericConstraints.Add(Function.GetGenericTypeConstraint());
}
if (_hasCustomStructParamSupport)
{
genericTypes.AddRange(_customStructParamTypes);
genericConstraints.AddRange(_customStructParamTypes.ConvertAll(paramType => $"MarshalledStruct<{paramType}>"));
}
string genericTypeString = string.Join(", ", genericTypes);
if (genericTypes.Count > 0)
{
builder.AppendLine($"{Modifiers}{returnType} {_functionName}<{genericTypeString}>({overload.ParamStringApiWithDefaults})");
builder.Indent();
foreach (var (genericType, constraint) in genericTypes.Zip(genericConstraints))
builder.AppendLine($"where {genericType} : {constraint}");
builder.UnIndent();
}
else
{
builder.AppendLine($"{Modifiers}{returnType} {_functionName}({overload.ParamStringApiWithDefaults})");
}
builder.OpenBrace();
overload.Translator?.ExportCppDefaultParameterAsLocalVariable(builder, overload.CSharpParamName, overload.CppDefaultValue, Function, overload.Parameter);
if (genericTypes.Count > 0)
{
builder.AppendLine($"{returnStatement}{_functionName}<{genericTypeString}>({overload.ParamsStringCall});");
}
else
{
builder.AppendLine($"{returnStatement}{_functionName}({overload.ParamsStringCall});");
}
builder.CloseBrace();
}
}
void ExportFunction(GeneratorStringBuilder builder)
{
builder.AppendLine();
ExportDeprecation(builder);
ExportSpecializationGetter(builder);
ExportSignature(builder, Modifiers);
builder.OpenBrace();
if (Throwing)
{
builder.AppendLine($"throw new InvalidOperationException(\"Function {Function.EngineName} cannot be called on a Blueprint-only implementer\");");
}
else if (BlueprintEvent)
{
builder.AppendLine($"if ({InstanceFunctionPtr} == IntPtr.Zero)");
builder.OpenBrace();
builder.AppendLine($"{InstanceFunctionPtr} = {ExporterCallbacks.UClassCallbacks}.CallGetNativeFunctionFromInstanceAndName(NativeObject, \"{Function.EngineName}\");");
builder.CloseBrace();
if (BlueprintImplementableEvent)
{
builder.AppendLine($"if (!{ExporterCallbacks.UFunctionCallbacks}.IsFunctionImplemented({InstanceFunctionPtr}))");
builder.OpenBrace();
builder.AppendLine($"throw new System.NotImplementedException(\"Tried calling {Function.Outer!.EngineName}.{_functionName} which is not implemented in C# or Blueprint!\");");
builder.CloseBrace();
}
ExportInvoke(builder, InstanceFunctionPtr);
}
else
{
ExportInvoke(builder);
}
builder.CloseBrace();
builder.AppendLine();
}
public void ExportInvoke(GeneratorStringBuilder builder, string functionPtr = "")
{
builder.BeginUnsafeBlock();
string nativeFunctionIntPtr = string.IsNullOrEmpty(functionPtr) ? NativeFunctionIntPtr : functionPtr;
if (!Function.HasParametersOrReturnValue())
{
if (string.IsNullOrEmpty(_customInvoke))
{
builder.AppendLine($"{_invokeFunction}({_invokeFirstArgument}, {nativeFunctionIntPtr}, {ScriptGeneratorUtilities.IntPtrZero}, {ScriptGeneratorUtilities.IntPtrZero});");
}
else
{
builder.AppendLine(_customInvoke);
}
}
else
{
if (_hasCustomStructParamSupport)
{
string genericTypes = string.Join(", ", _customStructParamTypes);
builder.AppendLine($"IntPtr Specialization = {Function.SourceName}_GetSpecialization<{genericTypes}>();");
builder.AppendStackAllocFunction($"{Function.SourceName}_ParamsSize<{genericTypes}>()",
"Specialization");
}
else
{
builder.AppendStackAllocFunction($"{Function.SourceName}_ParamsSize",
nativeFunctionIntPtr,
!BlittableFunction);
}
ForEachParameter((translator, parameter) =>
{
if (parameter.HasAllFlags(EPropertyFlags.ReturnParm))
{
return;
}
string propertyName = GetParameterName(parameter);
if (parameter.HasAllFlags(EPropertyFlags.ReferenceParm) || !parameter.HasAllFlags(EPropertyFlags.OutParm))
{
string offsetName = TryAddPrecedingCustomStructParams(parameter, parameter.GetOffsetVariableName());
translator.ExportToNative(builder, parameter, parameter.SourceName, "paramsBuffer", offsetName, propertyName);
}
});
builder.AppendLine();
if (string.IsNullOrEmpty(_customInvoke))
{
string invokedFunctionIntPtr = _hasCustomStructParamSupport ? "Specialization" : nativeFunctionIntPtr;
string returnValueAddressStr = Function.ReturnProperty != null
? $"paramsBuffer + {TryAddPrecedingCustomStructParams(Function.ReturnProperty, Function.ReturnProperty.GetOffsetVariableName())}"
: ScriptGeneratorUtilities.IntPtrZero;
builder.AppendLine($"{_invokeFunction}({_invokeFirstArgument}, {invokedFunctionIntPtr}, paramsBuffer, {returnValueAddressStr});");
}
else
{
builder.AppendLine(_customInvoke);
}
if (Function.ReturnProperty != null || Function.HasOutParams())
{
builder.AppendLine();
ForEachParameter((translator, parameter) =>
{
if (!parameter.HasAllFlags(EPropertyFlags.ReturnParm) &&
(parameter.HasAllFlags(EPropertyFlags.ConstParm) ||
!parameter.HasAllFlags(EPropertyFlags.OutParm)))
{
return;
}
string marshalDestination;
if (parameter.HasAllFlags(EPropertyFlags.ReturnParm))
{
builder.AppendLine($"{ReturnValueTranslator!.GetManagedType(parameter)} returnValue;");
marshalDestination = "returnValue";
}
else
{
marshalDestination = MakeOutMarshalDestination(parameter, translator, builder);
}
string offsetName = TryAddPrecedingCustomStructParams(parameter, parameter.GetOffsetVariableName());
translator.ExportFromNative(builder,
parameter,
parameter.SourceName,
$"{marshalDestination} =",
"paramsBuffer",
offsetName,
true,
parameter.HasAllFlags(EPropertyFlags.ReferenceParm) &&
!parameter.HasAllFlags(EPropertyFlags.ReturnParm));
});
}
builder.AppendLine();
ForEachParameter((translator, parameter) =>
{
if (!parameter.HasAnyFlags(EPropertyFlags.ReturnParm | EPropertyFlags.OutParm))
{
translator.ExportCleanupMarshallingBuffer(builder, parameter, parameter.SourceName);
}
});
ExportReturnStatement(builder);
}
builder.EndUnsafeBlock();
}
protected virtual string MakeOutMarshalDestination(UhtProperty parameter, PropertyTranslator propertyTranslator, GeneratorStringBuilder builder)
{
return GetParameterName(parameter);
}
protected virtual void ExportReturnStatement(GeneratorStringBuilder builder)
{
if (Function.ReturnProperty != null)
{
builder.AppendLine("return returnValue;");
}
}
void ExportSignature(GeneratorStringBuilder builder, string protection)
{
builder.AppendTooltip(Function);
AttributeBuilder attributeBuilder = new AttributeBuilder(Function);
if (BlueprintEvent)
{
attributeBuilder.AddArgument("FunctionFlags.BlueprintEvent");
}
attributeBuilder.AddGeneratedTypeAttribute(Function);
if (_hasGenericTypeSupport)
{
if (Function.HasMetadata("DeterminesOutputType"))
{
attributeBuilder.AddAttribute("UMetaData");
attributeBuilder.AddArgument($"\"DeterminesOutputType\"");
attributeBuilder.AddArgument($"\"{Function.GetMetadata("DeterminesOutputType")}\"");
}
if (Function.HasMetadata("DynamicOutputParam"))
{
attributeBuilder.AddAttribute("UMetaData");
attributeBuilder.AddArgument($"\"DynamicOutputParam\"");
attributeBuilder.AddArgument($"\"{Function.GetMetadata("DynamicOutputParam")}\"");
}
}
if (_hasCustomStructParamSupport)
{
attributeBuilder.AddAttribute("UMetaData");
attributeBuilder.AddArgument($"\"CustomStructureParam\"");
attributeBuilder.AddArgument($"\"{Function.GetMetadata("CustomStructureParam")}\"");
}
attributeBuilder.Finish();
builder.AppendLine(attributeBuilder.ToString());
string returnType = Function.ReturnProperty != null
? ReturnValueTranslator!.GetManagedType(Function.ReturnProperty)
: "void";
List<string> genericTypes = new List<string>();
List<string> genericConstraints = new List<string>();
if (_hasGenericTypeSupport)
{
genericTypes.Add("DOT");
genericConstraints.Add(Function.GetGenericTypeConstraint());
}
if (_hasCustomStructParamSupport)
{
genericTypes.AddRange(_customStructParamTypes);
genericConstraints.AddRange(_customStructParamTypes.ConvertAll(paramType => $"MarshalledStruct<{paramType}>"));
}
if (genericTypes.Count > 0)
{
builder.AppendLine($"{protection}{returnType} {_functionName}<{string.Join(", ", genericTypes)}>({_paramStringApiWithDefaults})");
builder.Indent();
foreach (var (genericType, constraint) in genericTypes.Zip(genericConstraints))
builder.AppendLine($"where {genericType} : {constraint}");
builder.UnIndent();
}
else
{
builder.AppendLine($"{protection}{returnType} {_functionName}({_paramStringApiWithDefaults})");
}
}
void ExportDeprecation(GeneratorStringBuilder builder)
{
if (Function.HasMetadata("DeprecatedFunction"))
{
string deprecationMessage = Function.GetMetadata("DeprecationMessage");
if (deprecationMessage.Length == 0)
{
deprecationMessage = "This function is deprecated.";
}
else
{
// Remove nested quotes
deprecationMessage = deprecationMessage.Replace("\"", "");
}
builder.AppendLine($"[Obsolete(\"{Function.SourceName} is deprecated: {deprecationMessage}\")]");
}
}
void ExportSpecializationGetter(GeneratorStringBuilder builder)
{
if (_hasCustomStructParamSupport)
{
int customStructureParamCount = _customStructParamTypes.Count;
string dictionaryKey = customStructureParamCount == 1
? "IntPtr"
: $"({string.Join(", ", Enumerable.Repeat("IntPtr", customStructureParamCount))})";
builder.AppendLine($"static Dictionary<{dictionaryKey}, IntPtr> {Function.SourceName}_Specializations = new Dictionary<{dictionaryKey}, IntPtr>();");
builder.AppendLine($"static IntPtr {Function.SourceName}_GetSpecialization<{string.Join(", ", _customStructParamTypes)}>()");
builder.Indent();
foreach (string customStructParamType in _customStructParamTypes)
{
builder.AppendLine($"where {customStructParamType} : MarshalledStruct<{customStructParamType}>");
}
builder.UnIndent();
builder.OpenBrace();
builder.AppendLine("IntPtr specializationNativeFunction;");
List<string> nativeClassPtrs = _customStructParamTypes.ConvertAll(customStructParamType =>
$"{customStructParamType}.GetNativeClassPtr()");
string specializationKeyInitializer = nativeClassPtrs.Count == 1 ? nativeClassPtrs[0] : $"({string.Join(", ", nativeClassPtrs)})";
builder.AppendLine($"{dictionaryKey} specializationKey = {specializationKeyInitializer};");
builder.AppendLine($"if(!{Function.SourceName}_Specializations.TryGetValue(specializationKey, out specializationNativeFunction))");
builder.OpenBrace();
builder.BeginUnsafeBlock();
string customStructBufferInitializationList = customStructureParamCount == 1 ? "specializationKey" : string.Join(", ", Enumerable.Range(1, customStructureParamCount).ToList().ConvertAll(i => $"specializationKey.Item{i}"));
builder.AppendLine($"IntPtr* customStructBufferAllocation = stackalloc IntPtr[]{{{customStructBufferInitializationList}}};");
builder.AppendLine("IntPtr customStructBuffer = (IntPtr) customStructBufferAllocation;");
string nativeFunctionIntPtr = $"{Function.SourceName}_NativeFunction";
string customStructNativePropertiesIntPtr = $"{Function.SourceName}_CustomStructureNativeProperties";
builder.AppendLine($"fixed(nint* nativePropertyBuffer = {customStructNativePropertiesIntPtr})");
builder.OpenBrace();
builder.AppendLine($"specializationNativeFunction = {ExporterCallbacks.UFunctionCallbacks}.CallCreateNativeFunctionCustomStructSpecialization({nativeFunctionIntPtr}, (nint) nativePropertyBuffer, customStructBuffer);");
builder.CloseBrace();
builder.AppendLine($"{Function.SourceName}_Specializations.Add(specializationKey, specializationNativeFunction);");
builder.EndUnsafeBlock();
builder.CloseBrace();
builder.AppendLine("return specializationNativeFunction;");
builder.CloseBrace();
}
}
void DetermineProtectionMode()
{
switch (_protectionMode)
{
case EFunctionProtectionMode.UseUFunctionProtection:
if (Function.HasAnyFlags(EFunctionFlags.Public | EFunctionFlags.BlueprintCallable))
{
Modifiers = ScriptGeneratorUtilities.PublicKeyword;
}
else if (Function.HasAllFlags(EFunctionFlags.Protected) || Function.HasMetadata("BlueprintProtected"))
{
Modifiers = ScriptGeneratorUtilities.ProtectedKeyword;
}
else
{
Modifiers = ScriptGeneratorUtilities.PrivateKeyword;
}
break;
case EFunctionProtectionMode.OverrideWithInternal:
Modifiers = "internal ";
break;
}
}
string DetermineInvokeFunction()
{
string invokeFunction = ExporterCallbacks.UObjectCallbacks;
if (Function.HasAllFlags(EFunctionFlags.Static))
{
return invokeFunction + ".CallInvokeNativeStaticFunction";
}
if (Function.HasAllFlags(EFunctionFlags.Net))
{
return invokeFunction + ".CallInvokeNativeNetFunction";
}
if (Function.HasAllFlags(EFunctionFlags.HasOutParms) || Function.HasReturnProperty)
{
return invokeFunction + ".CallInvokeNativeFunctionOutParms";
}
return invokeFunction + ".CallInvokeNativeFunction";
}
public string TryAddPrecedingCustomStructParams(UhtProperty parameter, string name)
{
if (!_hasCustomStructParamSupport)
{
return name;
}
int precedingCustomStructParams = parameter.GetPrecedingCustomStructParams();
if (precedingCustomStructParams > 0)
{
return name + $"<{string.Join(", ", _customStructParamTypes.GetRange(0, precedingCustomStructParams))}>()";
}
return name;
}
}