Ejemplo n.º 1
0
        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);
            }
        }
Ejemplo n.º 2
0
        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));
        }