private void AppendFunctionBody(CSharpTextBuilder builder, UFunction function, bool isGetter, bool isSetter, bool perInstanceFunctionAddress, List <string> namespaces) { string functionName = GetFunctionName(function); UProperty blueprintReturnProperty = function.GetBlueprintReturnProperty(); bool isDelegate = function.HasAnyFunctionFlags(EFunctionFlags.Delegate | EFunctionFlags.MulticastDelegate); bool isStatic = function.HasAnyFunctionFlags(EFunctionFlags.Static); string targetAddress = isStatic ? Settings.VarNames.ClassAddress : Names.UObject_Address; string ownerName = isDelegate || isStatic ? "null" : "this"; string invokeFunction = isStatic ? Names.NativeReflection_InvokeStaticFunction : Names.NativeReflection_InvokeFunction; if (isGetter) { functionName += "_getter"; } else if (isSetter) { functionName += "_setter"; } string functionAddressName = functionName + (perInstanceFunctionAddress ? Settings.VarNames.InstanceFunctionAddress : Settings.VarNames.FunctionAddress); Dictionary <UProperty, string> paramNames = GetParamNames(function); if (isSetter) { System.Diagnostics.Debug.Assert(paramNames.Count == 1); } if (Settings.CheckObjectDestroyed && !isStatic && !function.HasAnyFunctionFlags(EFunctionFlags.Delegate | EFunctionFlags.MulticastDelegate)) { builder.AppendLine(Names.UObject_CheckDestroyed + "();"); } if (Settings.GenerateIsValidSafeguards) { builder.AppendLine("if (!" + functionName + Settings.VarNames.IsValid + ")"); builder.OpenBrace(); builder.AppendLine(Names.NativeReflection_LogInvalidFunctionAccessed + "(\"" + function.GetPathName() + "\");"); AppendFunctionBodyDefaultValues(builder, function, blueprintReturnProperty, false, true, paramNames, namespaces); builder.CloseBrace(); } if (perInstanceFunctionAddress) { builder.AppendLine("if (" + functionAddressName + " == IntPtr.Zero)"); builder.OpenBrace(); builder.AppendLine(functionAddressName + " = " + Names.NativeReflection_GetFunctionFromInstance + "(" + targetAddress + ", \"" + function.GetName() + "\");"); builder.CloseBrace(); } if (isDelegate) { builder.AppendLine("if (IsBound)"); builder.OpenBrace(); } builder.AppendLine("unsafe"); builder.OpenBrace(); builder.AppendLine("byte* " + Settings.VarNames.ParamsBufferAllocation + " = stackalloc byte[" + functionName + Settings.VarNames.ParamsSize + "];"); builder.AppendLine("IntPtr " + Settings.VarNames.ParamsBuffer + " = new IntPtr(" + Settings.VarNames.ParamsBufferAllocation + ");"); if (Settings.LazyFunctionParamInitDestroy) { builder.AppendLine(Names.NativeReflection_InvokeFunction_InitAll + "(" + functionAddressName + ", " + Settings.VarNames.ParamsBuffer + ");"); } else if (Settings.MemzeroStackalloc || Settings.MemzeroStackallocOnlyIfOut) { bool requiresMemzero = Settings.MemzeroStackalloc; if (Settings.MemzeroStackallocOnlyIfOut) { foreach (KeyValuePair <UProperty, string> param in paramNames) { UProperty parameter = param.Key; string paramName = param.Value; // Memzero only if there is a return value or a (non ref) out param which doesn't have a zero constructor. // (if the param can't be zero initialized it will be initialized with a call to InitializeValue anyway) if (parameter.HasAnyPropertyFlags(EPropertyFlags.ReturnParm | EPropertyFlags.OutParm) && !parameter.HasAnyPropertyFlags(EPropertyFlags.ReferenceParm) && !parameter.HasAnyPropertyFlags(EPropertyFlags.ZeroConstructor)) { requiresMemzero = true; break; } } } if (requiresMemzero) { builder.AppendLine(Names.FMemory_Memzero + "(" + Settings.VarNames.ParamsBuffer + ", " + functionName + Settings.VarNames.ParamsSize + ");"); } } bool hasRefOrOutParam = false; bool hasReturn = false; bool hasParamWithDtor = false; foreach (KeyValuePair <UProperty, string> param in paramNames) { UProperty parameter = param.Key; string paramName = param.Value; if (parameter.HasAnyPropertyFlags(EPropertyFlags.ReturnParm) || parameter == blueprintReturnProperty) { hasReturn = true; continue; } else if (parameter.HasAnyPropertyFlags(EPropertyFlags.ReferenceParm | EPropertyFlags.OutParm)) { hasRefOrOutParam = true; } if (!Settings.LazyFunctionParamInitDestroy) { if (!parameter.HasAnyPropertyFlags(EPropertyFlags.ZeroConstructor)) { // Initialize values which don't have a zero constructor (this is required even though we will follow this up by a call // to ToNative as some structs have vtables e.g. FSlateBrush has its dtor in the vtable) builder.AppendLine(Names.NativeReflection_InitializeValue_InContainer + "(" + functionName + "_" + paramName + Settings.VarNames.PropertyAddress + "." + Names.UFieldAddress_Address + ", " + Settings.VarNames.ParamsBuffer + ");"); } if (!parameter.HasAnyPropertyFlags(EPropertyFlags.NoDestructor)) { // Parameter requires destruction hasParamWithDtor = true; } } if (parameter.HasAnyPropertyFlags(EPropertyFlags.Parm) && (!parameter.HasAnyPropertyFlags(EPropertyFlags.OutParm) || parameter.HasAnyPropertyFlags(EPropertyFlags.ReferenceParm))) { AppendPropertyToNative(builder, parameter, functionName + "_" + paramName, Settings.VarNames.ParamsBuffer, ownerName, isSetter ? "value" : paramName, true, namespaces); } } builder.AppendLine(); if (isDelegate) { builder.AppendLine(Names.FDelegateBase_ProcessDelegate + "(" + Settings.VarNames.ParamsBuffer + ");"); } else { builder.AppendLine(invokeFunction + "(" + targetAddress + ", " + functionAddressName + ", " + Settings.VarNames.ParamsBuffer + ", " + functionName + Settings.VarNames.ParamsSize + ");"); } if (hasReturn || hasRefOrOutParam || hasParamWithDtor) { builder.AppendLine(); foreach (KeyValuePair <UProperty, string> param in paramNames) { UProperty parameter = param.Key; string paramName = param.Value; // If this is function is collapsed into a setter property then we can skip the FromNative calls as there shouldn't be // anything we need to extract back out (if there is, then using a setter instead of a function is incorrect in that case) if (!isSetter) { if (parameter.HasAnyPropertyFlags(EPropertyFlags.ReturnParm) || parameter == blueprintReturnProperty) { AppendPropertyFromNative(builder, parameter, functionName + "_" + paramName, Settings.VarNames.ParamsBuffer, GetTypeName(parameter, namespaces) + " " + Settings.VarNames.ReturnResult, ownerName, true, namespaces); } else if (parameter.HasAnyPropertyFlags(EPropertyFlags.ReferenceParm | EPropertyFlags.OutParm)) { AppendPropertyFromNative(builder, parameter, functionName + "_" + paramName, Settings.VarNames.ParamsBuffer, paramName, ownerName, true, namespaces); } } if (!Settings.LazyFunctionParamInitDestroy && !parameter.HasAnyPropertyFlags(EPropertyFlags.NoDestructor)) { // Parameter requires destruction builder.AppendLine(Names.NativeReflection_DestroyValue_InContainer + "(" + functionName + "_" + paramName + Settings.VarNames.PropertyAddress + "." + Names.UFieldAddress_Address + ", " + Settings.VarNames.ParamsBuffer + ");"); } } } if (Settings.LazyFunctionParamInitDestroy) { builder.AppendLine(Names.NativeReflection_InvokeFunction_DestroyAll + "(" + functionAddressName + ", " + Settings.VarNames.ParamsBuffer + ");"); } if (hasReturn) { builder.AppendLine("return " + Settings.VarNames.ReturnResult + ";"); } builder.CloseBrace(); if (isDelegate) { builder.CloseBrace(); AppendFunctionBodyDefaultValues(builder, function, blueprintReturnProperty, true, false, paramNames, namespaces); } }
private string GetFunctionSignature(UnrealModuleInfo module, UFunction function, UStruct owner, string customFunctionName, string customModifiers, bool stripAdditionalText, bool isImplementationMethod, List <string> namespaces) { bool isInterface = owner != null && owner.IsChildOf <UInterface>(); bool isDelegate = function.HasAnyFunctionFlags(EFunctionFlags.Delegate | EFunctionFlags.MulticastDelegate); bool isStatic = function.HasAnyFunctionFlags(EFunctionFlags.Static); bool isBlueprintType = owner != null && owner.IsA <UBlueprintGeneratedClass>(); StringBuilder modifiers = new StringBuilder(); if (!string.IsNullOrEmpty(customModifiers)) { modifiers.Append(customModifiers); } else if (isInterface) { // Don't provide any modifiers for interfaces if there isn't one already provided } else { UFunction originalFunction; // NOTE: "isImplementationMethod" is talking about "_Implementation" methods. // "isInterfaceImplementation" is talking about regular methods which are implementations for a interface. bool isInterfaceImplementation; UClass originalOwner = GetOriginalFunctionOwner(function, out originalFunction, out isInterfaceImplementation); // All interface functions in the chain need to be public bool isInterfaceFunc = originalOwner != owner && originalOwner.HasAnyClassFlags(EClassFlags.Interface); if (isImplementationMethod || (function.HasAnyFunctionFlags(EFunctionFlags.Protected) && !isInterfaceFunc && !isDelegate)) { modifiers.Append("protected"); } else { modifiers.Append("public"); } if (isDelegate) { modifiers.Append(" delegate"); } if (isStatic) { modifiers.Append(" static"); } if (!isDelegate && !isStatic) { if (function.HasAnyFunctionFlags(EFunctionFlags.BlueprintEvent)) { UFunction superFunc = function.GetSuperFunction(); if (superFunc != null) { modifiers.Append(" override"); } else { // This have should been filtered out in CanExportFunction() Debug.Assert(originalOwner == owner || isInterfaceImplementation); // Explicit will have the _Implementation as virtual and the function declaration as // non-virtual (which is the same as C++) if (!Settings.UseExplicitImplementationMethods || isImplementationMethod) { modifiers.Append(" virtual"); } } } else { if (originalOwner != owner && !isInterfaceFunc) { // Add "new" if the parent class has a function with the same name but not BlueprintEvent. // (don't do this for interface functions as we are implementing the function not redefining it) modifiers.Append(" new"); } } } } string returnType = "void"; int numReturns = 0; StringBuilder parameters = new StringBuilder(); // index is the index into parameters string Dictionary <int, string> parameterDefaultsByIndex = new Dictionary <int, string>(); // Once this is true all parameters from that point should also have defaults bool hasDefaultParameters = false; // Blueprint can define ref/out parameters with default values, this can't be translated to code bool invalidDefaultParams = false; // Info so we can avoid param name conflicts Dictionary <UProperty, string> paramNames = GetParamNames(function); // Generic array parameters string[] arrayParamNames = function.GetCommaSeperatedMetaData("ArrayParam"); // Generic parameters depending on array type string[] arrayTypeDependentParamNames = function.GetCommaSeperatedMetaData("ArrayTypeDependentParams"); // AutoCreateRefTerm will force ref on given parameter names (comma seperated) string[] autoRefParamNames = function.GetCommaSeperatedMetaData("AutoCreateRefTerm"); // If this is a blueprint type try and getting the return value from the first out param (if there is only one out) UProperty blueprintReturnProperty = function.GetBlueprintReturnProperty(); bool firstParameter = true; foreach (KeyValuePair <UProperty, string> param in paramNames) { UProperty parameter = param.Key; string paramName = param.Value; string rawParamName = parameter.GetName(); if (!parameter.HasAnyPropertyFlags(EPropertyFlags.Parm)) { continue; } if (parameter.HasAnyPropertyFlags(EPropertyFlags.ReturnParm) || parameter == blueprintReturnProperty) { returnType = GetTypeName(parameter, namespaces); numReturns++; } else { if (firstParameter) { firstParameter = false; } else { parameters.Append(", "); } if (!parameter.HasAnyPropertyFlags(EPropertyFlags.ConstParm)) { if (parameter.HasAnyPropertyFlags(EPropertyFlags.ReferenceParm) || autoRefParamNames.Contains(rawParamName)) { parameters.Append("ref "); } else if (parameter.HasAnyPropertyFlags(EPropertyFlags.OutParm)) { parameters.Append("out "); } } string paramTypeName = GetTypeName(parameter, namespaces); if (arrayParamNames.Contains(rawParamName)) { int genericsIndex = paramTypeName.IndexOf('<'); if (genericsIndex >= 0) { paramTypeName = paramTypeName.Substring(0, genericsIndex) + "<T>"; } } else if (arrayTypeDependentParamNames.Contains(rawParamName)) { paramTypeName = "T"; } parameters.Append(paramTypeName + " " + paramName); if (!invalidDefaultParams) { string defaultValue = GetParamDefaultValue(function, parameter, paramTypeName, ref hasDefaultParameters, ref invalidDefaultParams); if (!string.IsNullOrEmpty(defaultValue) && !invalidDefaultParams) { if (isBlueprintType && (parameter.HasAnyPropertyFlags(EPropertyFlags.ReferenceParm | EPropertyFlags.OutParm) || autoRefParamNames.Contains(rawParamName))) { invalidDefaultParams = true; } else { if (!hasDefaultParameters) { hasDefaultParameters = true; } parameterDefaultsByIndex[parameters.Length] = " = " + defaultValue; } } } } } if (numReturns > 1) { FMessage.Log(ELogVerbosity.Error, "More than 1 return on function '" + function.GetPathName() + "'"); } // Insert the default parameters if they aren't invalid if (!invalidDefaultParams) { int offset = 0; foreach (KeyValuePair <int, string> parameterDefault in parameterDefaultsByIndex) { parameters.Insert(parameterDefault.Key + offset, parameterDefault.Value); offset += parameterDefault.Value.Length; } } string functionName = GetFunctionName(function); string additionalStr = string.Empty; if (isDelegate) { functionName = GetTypeNameDelegate(function); additionalStr = ";"; } //if (isInterface) //{ // additionalStr = ";"; //} if (isImplementationMethod) { functionName += Settings.VarNames.ImplementationMethod; } if (!string.IsNullOrEmpty(customFunctionName)) { functionName = customFunctionName; } if (stripAdditionalText) { additionalStr = string.Empty; } string generics = string.Empty; if (arrayParamNames.Length > 0 || arrayTypeDependentParamNames.Length > 0) { generics = "<T>"; } if (modifiers.Length > 0) { modifiers.Append(' '); } return(string.Format("{0}{1} {2}{3}({4}){5}", modifiers, returnType, functionName, generics, parameters, additionalStr)); }