Files
UnrealEngine/Engine/Source/Programs/Shared/EpicGames.UHT/Parsers/UhtFunctionParser.cs
2025-05-18 13:04:45 +08:00

708 lines
25 KiB
C#

// Copyright Epic Games, Inc. All Rights Reserved.
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using EpicGames.Core;
using EpicGames.UHT.Tables;
using EpicGames.UHT.Tokenizer;
using EpicGames.UHT.Types;
using EpicGames.UHT.Utils;
namespace EpicGames.UHT.Parsers
{
/**
* AdvancedDisplay can be used in two ways:
* 1. 'AdvancedDisplay = "3"' - the number tells how many parameters (from beginning) should NOT BE marked
* 2. 'AdvancedDisplay = "AttachPointName, Location, LocationType"' - list the parameters, that should BE marked
*/
struct UhtAdvancedDisplayParameterHandler
{
private readonly UhtMetaData _metaData;
private readonly string[]? _parameterNames;
private readonly int _numberLeaveUnmarked;
private readonly bool _bUseNumber;
private int _alreadyLeft;
public UhtAdvancedDisplayParameterHandler(UhtMetaData metaData)
{
_metaData = metaData;
_parameterNames = null;
_numberLeaveUnmarked = -1;
_alreadyLeft = 0;
_bUseNumber = false;
if (_metaData.TryGetValue(UhtNames.AdvancedDisplay, out string? foundString))
{
_parameterNames = foundString.ToString().Split(',', StringSplitOptions.RemoveEmptyEntries);
for (int index = 0, endIndex = _parameterNames.Length; index < endIndex; ++index)
{
_parameterNames[index] = _parameterNames[index].Trim();
}
if (_parameterNames.Length == 1)
{
_bUseNumber = Int32.TryParse(_parameterNames[0], out _numberLeaveUnmarked);
}
}
}
/**
* return if given parameter should be marked as Advance View,
* the function should be called only once for any parameter
*/
public bool ShouldMarkParameter(StringView parameterName)
{
if (_bUseNumber)
{
if (_numberLeaveUnmarked < 0)
{
return false;
}
if (_alreadyLeft < _numberLeaveUnmarked)
{
++_alreadyLeft;
return false;
}
return true;
}
if (_parameterNames == null)
{
return false;
}
foreach (string element in _parameterNames)
{
if (parameterName.Span.Equals(element, StringComparison.OrdinalIgnoreCase))
{
return true;
}
}
return false;
}
/** return if more parameters can be marked */
public readonly bool CanMarkMore()
{
return _bUseNumber ? _numberLeaveUnmarked > 0 : (_parameterNames != null && _parameterNames.Length > 0);
}
}
/// <summary>
/// UFUNCTION parser
/// </summary>
[UnrealHeaderTool]
public static class UhtFunctionParser
{
#region Keywords
[UhtKeyword(Extends = UhtTableNames.Global)]
[UhtKeyword(Extends = UhtTableNames.Class)]
[SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "Attribute accessed method")]
[SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "Attribute accessed method")]
private static UhtParseResult UDELEGATEKeyword(UhtParsingScope topScope, UhtParsingScope actionScope, ref UhtToken token)
{
return ParseUDelegate(topScope, token, true);
}
[UhtKeyword(Extends = UhtTableNames.Class)]
[UhtKeyword(Extends = UhtTableNames.Interface)]
[UhtKeyword(Extends = UhtTableNames.NativeInterface)]
[SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "Attribute accessed method")]
[SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "Attribute accessed method")]
private static UhtParseResult UFUNCTIONKeyword(UhtParsingScope topScope, UhtParsingScope actionScope, ref UhtToken token)
{
return ParseUFunction(topScope, token);
}
[UhtKeywordCatchAll(Extends = UhtTableNames.Global)]
[SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "Attribute accessed method")]
private static UhtParseResult ParseCatchAllKeyword(UhtParsingScope topScope, ref UhtToken token)
{
if (UhtFunctionParser.IsValidateDelegateDeclaration(token))
{
return ParseUDelegate(topScope, token, false);
}
return UhtParseResult.Unhandled;
}
#endregion
private static UhtParseResult ParseUDelegate(UhtParsingScope parentScope, UhtToken token, bool hasSpecifiers)
{
UhtFunction function = new(parentScope.HeaderFile, parentScope.ScopeType, token.InputLine);
{
using UhtParsingScope topScope = new(parentScope, function, parentScope.Session.GetKeywordTable(UhtTableNames.Function), UhtAccessSpecifier.Public);
const string ScopeName = "delegate declaration";
{
using UhtMessageContext tokenContext = new(ScopeName);
topScope.AddModuleRelativePathToMetaData();
UhtSpecifierContext specifierContext = new(topScope, topScope.TokenReader, function.MetaData);
UhtSpecifierParser specifiers = UhtSpecifierParser.GetThreadInstance(specifierContext, ScopeName, parentScope.Session.GetSpecifierTable(UhtTableNames.Function));
// If this is a UDELEGATE, parse the specifiers
StringView delegateMacro = new();
if (hasSpecifiers)
{
specifiers.ParseSpecifiers();
specifiers.ParseDeferred();
FinalizeFunctionSpecifiers(function);
UhtToken macroToken = topScope.TokenReader.GetToken();
if (!IsValidateDelegateDeclaration(macroToken))
{
throw new UhtTokenException(topScope.TokenReader, macroToken, "delegate macro");
}
delegateMacro = macroToken.Value;
}
else
{
delegateMacro = token.Value;
}
// Break the delegate declaration macro down into parts
bool hasReturnValue = delegateMacro.Span.Contains("_RetVal".AsSpan(), StringComparison.Ordinal);
bool declaredConst = delegateMacro.Span.Contains("_Const".AsSpan(), StringComparison.Ordinal);
bool isMulticast = delegateMacro.Span.Contains("_MULTICAST".AsSpan(), StringComparison.Ordinal);
bool isSparse = delegateMacro.Span.Contains("_SPARSE".AsSpan(), StringComparison.Ordinal);
// Determine the parameter count
int foundParamIndex = topScope.Session.Config!.FindDelegateParameterCount(delegateMacro);
// Try reconstructing the string to make sure it matches our expectations
string expectedOriginalString = String.Format("DECLARE_DYNAMIC{0}{1}_DELEGATE{2}{3}{4}",
isMulticast ? "_MULTICAST" : "",
isSparse ? "_SPARSE" : "",
hasReturnValue ? "_RetVal" : "",
topScope.Session.Config!.GetDelegateParameterCountString(foundParamIndex),
declaredConst ? "_Const" : "");
if (delegateMacro != expectedOriginalString)
{
throw new UhtException(topScope.TokenReader, $"Unable to parse delegate declaration; expected '{expectedOriginalString}' but found '{delegateMacro}'.");
}
// Multi-cast delegate function signatures are not allowed to have a return value
if (hasReturnValue && isMulticast)
{
throw new UhtException(topScope.TokenReader, "Multi-cast delegates function signatures must not return a value");
}
// Delegate signature
function.FunctionType = isSparse ? UhtFunctionType.SparseDelegate : UhtFunctionType.Delegate;
function.FunctionFlags |= EFunctionFlags.Public | EFunctionFlags.Delegate;
if (isMulticast)
{
function.FunctionFlags |= EFunctionFlags.MulticastDelegate;
}
// Now parse the macro body
topScope.TokenReader.Require('(');
// Parse the return type
UhtProperty? returnValueProperty = null;
if (hasReturnValue)
{
UhtPropertyParser.Parse(topScope, EPropertyFlags.None,
function.GetPropertyParseOptions(true), UhtPropertyCategory.Return,
(UhtParsingScope topScope, UhtProperty property, ref UhtToken nameToken, UhtLayoutMacroType layoutMacroType) =>
{
property.PropertyFlags |= EPropertyFlags.Parm | EPropertyFlags.OutParm | EPropertyFlags.ReturnParm;
returnValueProperty = property;
});
topScope.TokenReader.Require(',');
}
// Skip white spaces to get InputPos exactly on beginning of function name.
topScope.TokenReader.SkipWhitespaceAndComments();
// Get the delegate name
UhtToken funcNameToken = topScope.TokenReader.GetIdentifier("name");
function.SourceName = funcNameToken.Value.ToString();
// If this is a delegate function then go ahead and mangle the name so we don't collide with
// actual functions or properties
{
//@TODO: UCREMOVAL: Eventually this mangling shouldn't occur
// Remove the leading F
if (function.SourceName[0] != 'F')
{
topScope.TokenReader.LogError("Delegate type declarations must start with F");
}
function.StrippedFunctionName = function.SourceName[1..];
function.EngineName = $"{function.StrippedFunctionName}{UhtFunction.GeneratedDelegateSignatureSuffix}";
}
SetFunctionNames(function);
AddFunction(function);
// determine whether this function should be 'const'
if (declaredConst)
{
function.FunctionFlags |= EFunctionFlags.Const;
function.FunctionExportFlags |= UhtFunctionExportFlags.DeclaredConst;
}
if (isSparse)
{
topScope.TokenReader.Require(',');
UhtToken name = topScope.TokenReader.GetIdentifier("OwningClass specifier");
UhtEngineNameParts parts = UhtUtilities.GetEngineNameParts(name.Value);
function.SparseOwningClassName = parts.EngineName.ToString();
topScope.TokenReader.Require(',');
function.SparseDelegateName = topScope.TokenReader.GetIdentifier("delegate name").Value.ToString();
}
// Get parameter list.
if (foundParamIndex >= 0)
{
topScope.TokenReader.Require(',');
ParseParameterList(topScope, function.GetPropertyParseOptions(false));
}
else
{
// Require the closing paren even with no parameter list
topScope.TokenReader.Require(')');
}
// Add back in the return value
if (returnValueProperty != null)
{
topScope.ScopeType.AddChild(returnValueProperty);
}
// Verify the number of parameters (FoundParamIndex = -1 means zero parameters, 0 means one, ...)
int expectedProperties = foundParamIndex + 1 + (hasReturnValue ? 1 : 0);
int propertiesCount = function.Properties.Count();
if (propertiesCount != expectedProperties)
{
throw new UhtException(topScope.TokenReader, $"Expected {expectedProperties} parameters but found {propertiesCount} parameters");
}
// The macro line must be set here
function.MacroLineNumber = topScope.TokenReader.InputLine;
// Try parsing metadata for the function
specifiers.ParseFieldMetaData();
topScope.AddFormattedCommentsAsTooltipMetaData();
// Consume a semicolon, it's not required for the delegate macro since it contains one internally
topScope.TokenReader.Require(';');
}
return UhtParseResult.Handled;
}
}
private static UhtParseResult ParseUFunction(UhtParsingScope parentScope, UhtToken token)
{
UhtFunction function = new(parentScope.HeaderFile, parentScope.ScopeType, token.InputLine);
{
using UhtParsingScope topScope = new(parentScope, function, parentScope.Session.GetKeywordTable(UhtTableNames.Function), UhtAccessSpecifier.Public);
UhtParsingScope outerClassScope = topScope.CurrentClassScope;
UhtClass outerClass = (UhtClass)outerClassScope.ScopeType;
string scopeName = "function";
{
using UhtMessageContext tokenContext = new(scopeName);
topScope.AddModuleRelativePathToMetaData();
UhtSpecifierContext specifierContext = new(topScope, topScope.TokenReader, function.MetaData);
UhtSpecifierParser specifierParser = UhtSpecifierParser.GetThreadInstance(specifierContext, scopeName, parentScope.Session.GetSpecifierTable(UhtTableNames.Function));
specifierParser.ParseSpecifiers();
if (!outerClass.ClassFlags.HasAnyFlags(EClassFlags.Native))
{
throw new UhtException(function, "Should only be here for native classes!");
}
function.MacroLineNumber = topScope.TokenReader.InputLine;
function.FunctionFlags |= EFunctionFlags.Native;
bool automaticallyFinal = true;
switch (outerClassScope.AccessSpecifier)
{
case UhtAccessSpecifier.Public:
function.FunctionFlags |= EFunctionFlags.Public;
break;
case UhtAccessSpecifier.Protected:
function.FunctionFlags |= EFunctionFlags.Protected;
break;
case UhtAccessSpecifier.Private:
function.FunctionFlags |= EFunctionFlags.Private | EFunctionFlags.Final;
// This is automatically final as well, but in a different way and for a different reason
automaticallyFinal = false;
break;
}
topScope.TokenReader.OptionalAttributes(false);
if (topScope.TokenReader.TryOptional("static"))
{
function.FunctionFlags |= EFunctionFlags.Static;
function.FunctionExportFlags |= UhtFunctionExportFlags.CppStatic;
}
if (function.MetaData.ContainsKey(UhtNames.CppFromBpEvent))
{
function.FunctionFlags |= EFunctionFlags.Event;
}
UhtCompilerDirective compilerDirective = topScope.HeaderParser.GetCurrentCompositeCompilerDirective();
if ((compilerDirective & UhtCompilerDirective.WithEditor) != 0)
{
function.FunctionFlags |= EFunctionFlags.EditorOnly;
function.DefineScope |= UhtDefineScope.EditorOnlyData;
}
function.DefineScope |= compilerDirective.GetDefaultDefineScopes();
specifierParser.ParseDeferred();
FinalizeFunctionSpecifiers(function);
if (function.FunctionExportFlags.HasAnyFlags(UhtFunctionExportFlags.CustomThunk) && !function.MetaData.ContainsKey(UhtNames.CustomThunk))
{
function.MetaData.Add(UhtNames.CustomThunk, true);
}
if (function.FunctionFlags.HasAnyFlags(EFunctionFlags.Net))
{
// Network replicated functions are always events, and are only final if sealed
scopeName = "event";
tokenContext.Reset(scopeName);
automaticallyFinal = false;
}
if (function.FunctionFlags.HasAnyFlags(EFunctionFlags.BlueprintEvent))
{
scopeName = function.FunctionFlags.HasAnyFlags(EFunctionFlags.Native) ? "BlueprintNativeEvent" : "BlueprintImplementableEvent";
tokenContext.Reset(scopeName);
automaticallyFinal = false;
}
// Record the tokens so we can detect this function as a declaration later (i.e. RPC)
{
using UhtTokenRecorder tokenRecorder = new(parentScope, function);
if (topScope.TokenReader.TryOptional("virtual"))
{
function.FunctionExportFlags |= UhtFunctionExportFlags.Virtual;
}
bool internalOnly = function.MetaData.GetBoolean(UhtNames.BlueprintInternalUseOnly);
// Peek ahead to look for a CORE_API style DLL import/export token if present
if (topScope.TokenReader.TryOptionalAPIMacro(out UhtToken apiMacroToken))
{
//@TODO: Validate the module name for RequiredAPIMacroIfPresent
function.FunctionFlags |= EFunctionFlags.RequiredAPI;
function.FunctionExportFlags |= UhtFunctionExportFlags.RequiredAPI;
}
// Look for static again, in case there was an ENGINE_API token first
if (apiMacroToken && topScope.TokenReader.TryOptional("static"))
{
topScope.TokenReader.LogError($"Unexpected API macro '{apiMacroToken.Value}'. Did you mean to put '{apiMacroToken.Value}' after the static keyword?");
}
// Look for virtual again, in case there was an ENGINE_API token first
if (topScope.TokenReader.TryOptional("virtual"))
{
function.FunctionExportFlags |= UhtFunctionExportFlags.Virtual;
}
// If virtual, remove the implicit final, the user can still specifying an explicit final at the end of the declaration
if (function.FunctionExportFlags.HasAnyFlags(UhtFunctionExportFlags.Virtual))
{
automaticallyFinal = false;
}
// Handle the initial implicit/explicit final
// A user can still specify an explicit final after the parameter list as well.
if (automaticallyFinal || function.FunctionExportFlags.HasAnyFlags(UhtFunctionExportFlags.SealedEvent))
{
function.FunctionFlags |= EFunctionFlags.Final;
function.FunctionExportFlags |= UhtFunctionExportFlags.Final | UhtFunctionExportFlags.AutoFinal;
}
// Get return type. C++ style functions always have a return value type, even if it's void
UhtToken funcNameToken = new();
UhtProperty? returnValueProperty = null;
UhtPropertyParser.Parse(topScope, EPropertyFlags.None,
function.GetPropertyParseOptions(true), UhtPropertyCategory.Return,
(UhtParsingScope topScope, UhtProperty property, ref UhtToken nameToken, UhtLayoutMacroType layoutMacroType) =>
{
property.PropertyFlags |= EPropertyFlags.Parm | EPropertyFlags.OutParm | EPropertyFlags.ReturnParm;
funcNameToken = nameToken;
if (property is not UhtVoidProperty)
{
returnValueProperty = property;
}
if (property.PropertyExportFlags.HasAnyFlags(UhtPropertyExportFlags.TVerseTask))
{
function.FunctionExportFlags |= UhtFunctionExportFlags.VerseSuspends;
}
});
if (funcNameToken.Value.Length == 0)
{
throw new UhtException(topScope.TokenReader, "expected return value and function name");
}
// Get function or operator name.
function.SourceName = funcNameToken.Value.ToString();
// If the function is a verse function, then we need to apply the source name to the verse name
if (function.IsVerseField && String.IsNullOrEmpty(function.VerseName))
{
function.VerseName = function.SourceName;
}
scopeName = $"{scopeName} '{function.SourceName}'";
tokenContext.Reset(scopeName);
topScope.TokenReader.Require('(');
SetFunctionNames(function);
AddFunction(function);
// Get parameter list.
ParseParameterList(topScope, function.GetPropertyParseOptions(false));
// Add back in the return value
if (returnValueProperty != null)
{
topScope.ScopeType.AddChild(returnValueProperty);
}
// determine whether this function should be 'const'
if (topScope.TokenReader.TryOptional("const"))
{
if (function.FunctionFlags.HasAnyFlags(EFunctionFlags.Native))
{
// @TODO: UCREMOVAL Reconsider?
//Throwf(TEXT("'const' may only be used for native functions"));
}
function.FunctionFlags |= EFunctionFlags.Const;
function.FunctionExportFlags |= UhtFunctionExportFlags.DeclaredConst;
}
// Try parsing metadata for the function
specifierParser.ParseFieldMetaData();
// COMPATIBILITY-TODO - Try to pull any comment following the declaration
topScope.TokenReader.PeekToken();
topScope.TokenReader.CommitPendingComments();
topScope.AddFormattedCommentsAsTooltipMetaData();
// 'final' and 'override' can appear in any order before an optional '= 0' pure virtual specifier
bool foundFinal = topScope.TokenReader.TryOptional("final");
bool foundOverride = topScope.TokenReader.TryOptional("override");
if (!foundFinal && foundOverride)
{
foundFinal = topScope.TokenReader.TryOptional("final");
}
// Handle C++ style functions being declared as abstract
if (topScope.TokenReader.TryOptional('='))
{
bool gotZero = topScope.TokenReader.TryOptionalConstInt(out int zeroValue);
gotZero = gotZero && (zeroValue == 0);
if (!gotZero || zeroValue != 0)
{
throw new UhtException(topScope.TokenReader, "Expected 0 to indicate function is abstract");
}
}
// Look for the final keyword to indicate this function is sealed
if (foundFinal)
{
// This is a final (prebinding, non-overridable) function
function.FunctionFlags |= EFunctionFlags.Final;
function.FunctionExportFlags |= UhtFunctionExportFlags.Final;
}
// Optionally consume a semicolon
// This is optional to allow inline function definitions
if (topScope.TokenReader.TryOptional(';'))
{
// Do nothing (consume it)
}
else if (topScope.TokenReader.TryPeekOptional('{'))
{
// Skip inline function bodies
UhtToken tokenCopy = new();
topScope.TokenReader.SkipDeclaration(ref tokenCopy);
}
}
}
return UhtParseResult.Handled;
}
}
private static void FinalizeFunctionSpecifiers(UhtFunction function)
{
if (function.FunctionFlags.HasAnyFlags(EFunctionFlags.Net))
{
// Network replicated functions are always events
function.FunctionFlags |= EFunctionFlags.Event;
}
}
internal static bool IsValidateDelegateDeclaration(UhtToken token)
{
return (token.IsIdentifier() && token.Value.Span.StartsWith("DECLARE_DYNAMIC_"));
}
private static void ParseParameterList(UhtParsingScope topScope, UhtPropertyParseOptions options)
{
UhtFunction function = (UhtFunction)topScope.ScopeType;
bool isNetFunc = function.FunctionFlags.HasAnyFlags(EFunctionFlags.Net);
UhtPropertyCategory propertyCategory = isNetFunc ? UhtPropertyCategory.ReplicatedParameter : UhtPropertyCategory.RegularParameter;
EPropertyFlags disallowFlags = ~(EPropertyFlags.ParmFlags | EPropertyFlags.AutoWeak | EPropertyFlags.RepSkip | EPropertyFlags.UObjectWrapper | EPropertyFlags.NativeAccessSpecifiers);
UhtAdvancedDisplayParameterHandler advancedDisplay = new(topScope.ScopeType.MetaData);
topScope.TokenReader.RequireList(')', ',', false, () =>
{
UhtPropertyParser.Parse(topScope, disallowFlags, options, propertyCategory,
(UhtParsingScope topScope, UhtProperty property, ref UhtToken nameToken, UhtLayoutMacroType layoutMacroType) =>
{
property.PropertyFlags |= EPropertyFlags.Parm;
if (advancedDisplay.CanMarkMore() && advancedDisplay.ShouldMarkParameter(property.EngineName))
{
property.PropertyFlags |= EPropertyFlags.AdvancedDisplay;
}
// Default value.
if (topScope.TokenReader.TryOptional('='))
{
List<UhtToken> defaultValueTokens = new();
int parenthesisNestCount = 0;
while (!topScope.TokenReader.IsEOF)
{
UhtToken token = topScope.TokenReader.PeekToken();
if (token.IsSymbol(','))
{
if (parenthesisNestCount == 0)
{
break;
}
defaultValueTokens.Add(token);
topScope.TokenReader.ConsumeToken();
}
else if (token.IsSymbol(')'))
{
if (parenthesisNestCount == 0)
{
break;
}
defaultValueTokens.Add(token);
topScope.TokenReader.ConsumeToken();
--parenthesisNestCount;
}
else if (token.IsSymbol('('))
{
++parenthesisNestCount;
defaultValueTokens.Add(token);
topScope.TokenReader.ConsumeToken();
}
else
{
defaultValueTokens.Add(token);
topScope.TokenReader.ConsumeToken();
}
}
// allow exec functions to be added to the metaData, this is so we can have default params for them.
bool storeCppDefaultValueInMetaData = function.FunctionFlags.HasAnyFlags(EFunctionFlags.BlueprintCallable | EFunctionFlags.Exec);
if (defaultValueTokens.Count > 0 && storeCppDefaultValueInMetaData)
{
property.DefaultValueTokens = defaultValueTokens;
}
}
});
});
}
private static void AddFunction(UhtFunction function)
{
function.Outer?.AddChild(function);
}
private static void SetFunctionNames(UhtFunction function)
{
// The source name won't have the suffix applied to delegate names, however, the engine name will
// We use the engine name because we need to detect the suffix for delegates
string functionName = function.EngineName;
if (functionName.EndsWith(UhtFunction.GeneratedDelegateSignatureSuffix, StringComparison.Ordinal))
{
functionName = functionName[..^UhtFunction.GeneratedDelegateSignatureSuffix.Length];
}
function.UnMarshalAndCallName = "exec" + functionName;
if (function.FunctionFlags.HasAnyFlags(EFunctionFlags.BlueprintEvent))
{
function.MarshalAndCallName = functionName;
if (function.FunctionFlags.HasAllFlags(EFunctionFlags.BlueprintEvent | EFunctionFlags.Native))
{
function.CppImplName = function.EngineName + "_Implementation";
}
}
else if (function.FunctionFlags.HasAllFlags(EFunctionFlags.Native | EFunctionFlags.Net))
{
function.MarshalAndCallName = functionName;
if (function.FunctionFlags.HasAnyFlags(EFunctionFlags.NetResponse))
{
// Response function implemented by programmer and called directly from thunk
function.CppImplName = function.EngineName;
}
else
{
if (function.CppImplName.Length == 0)
{
function.CppImplName = function.EngineName + "_Implementation";
}
else if (function.CppImplName == functionName)
{
function.LogError("Native implementation function must be different than original function name.");
}
if (function.CppValidationImplName.Length == 0 && function.FunctionFlags.HasAnyFlags(EFunctionFlags.NetValidate))
{
function.CppValidationImplName = function.EngineName + "_Validate";
}
else if (function.CppValidationImplName == functionName)
{
function.LogError("Validation function must be different than original function name.");
}
}
}
else if (function.FunctionFlags.HasAnyFlags(EFunctionFlags.Delegate))
{
function.MarshalAndCallName = "delegate" + functionName;
}
if (function.CppImplName.Length == 0)
{
function.CppImplName = functionName;
}
if (function.MarshalAndCallName.Length == 0)
{
function.MarshalAndCallName = "event" + functionName;
}
}
}
}