public void AddFunction(UFunction function, bool exportable) { allFunctions.Add(function); if (exportable) { exportableFunctions.Add(function); if (function.HasAnyFunctionFlags(EFunctionFlags.Static)) { HasStaticFunction = true; } } else { nonExportableFunctions.Add(function); if (function.HasAnyFunctionFlags(EFunctionFlags.Static)) { HasStaticNonExportableFunction = true; } } }
private void GenerateCodeForProperty(UnrealModuleInfo module, CSharpTextBuilder builder, CSharpTextBuilder offsetsBuilder, CollapsedMember collapsedMember, bool isBlueprintType, List <string> namespaces) { StringBuilder modifiers = new StringBuilder(); if (collapsedMember.BackingProperty != null) { UProperty property = collapsedMember.BackingProperty; if (property.HasAnyPropertyFlags(EPropertyFlags.DisableEditOnInstance) && !property.GetBoolMetaData(MDProp.AllowPrivateAccess)) { modifiers.Append("private"); } else if (property.HasAnyPropertyFlags(EPropertyFlags.NativeAccessSpecifierProtected | EPropertyFlags.Protected)) { modifiers.Append("protected"); } else { modifiers.Append("public"); } } else { UFunction function = collapsedMember.Getter != null ? collapsedMember.Getter : collapsedMember.Setter; if (function.HasAnyFunctionFlags(EFunctionFlags.Protected)) { modifiers.Append("protected"); } else { modifiers.Append("public"); } } if (modifiers.Length > 0) { modifiers.Append(" "); } // Note: Potential issues with different categories/docs/attribute on BackingProperty/Getter/Setter UField field = collapsedMember.BackingProperty; if (field == null) { field = collapsedMember.Getter; if (field == null) { field = collapsedMember.Setter; } } // Use either the backing property or the getter function for the documentation UField fieldForDocumentation = collapsedMember.BackingProperty != null ? (UField)collapsedMember.BackingProperty : collapsedMember.Getter; string name = collapsedMember.ResolvedName != null ? collapsedMember.ResolvedName : collapsedMember.Name; string propertyName = GetName(field, name, Settings.MemberCasing, false, true); AppendGetterSetterOffsets(builder, offsetsBuilder, propertyName, collapsedMember.Getter == null || collapsedMember.Setter == null ? collapsedMember.BackingProperty : null, namespaces, collapsedMember.Getter, collapsedMember.Setter); AppendDocComment(builder, fieldForDocumentation, isBlueprintType); AppendAttribute(builder, field, module, true); builder.AppendLine(modifiers + GetTypeName(collapsedMember.Property, namespaces) + " " + propertyName); builder.OpenBrace(); if (collapsedMember.Getter != null) { AppendGetter(builder, propertyName, collapsedMember.Getter, namespaces); } else if (collapsedMember.BackingProperty != null) { AppendGetter(builder, propertyName, collapsedMember.BackingProperty, namespaces); } if (collapsedMember.Setter != null) { AppendSetter(builder, propertyName, collapsedMember.Setter, namespaces); } else if (collapsedMember.BackingProperty != null) { AppendSetter(builder, propertyName, collapsedMember.BackingProperty, namespaces); } builder.CloseBrace(); builder.AppendLine(); }
private void AppendAttribute(CSharpTextBuilder builder, UField field, UnrealModuleInfo module, bool isCollapsedMember) { UnrealModuleType moduleType; UnrealModuleType moduleAssetType; string moduleName = GetModuleName(field, out moduleType, out moduleAssetType); if (string.IsNullOrEmpty(moduleName)) { moduleName = module.Name; } List <string> attributes = new List <string>(); // TODO: Combine all of this into EPropertyType (add some TypeCode into UField?) bool isInterface = false; UEnum unrealEnum = field as UEnum; UClass unrealClass = field as UClass; UScriptStruct unrealStruct = field as UScriptStruct; UFunction unrealFunction = field as UFunction; if (unrealFunction != null) { if (unrealFunction.HasAnyFunctionFlags(EFunctionFlags.Delegate)) { attributes.Add("UDelegate"); } else { string additionalFunctionInfo = string.Empty; // TODO: Only get the script name for virtual functions / interface functions as we currently only need // this for finding the base function for hooking things up to the native base types. string scriptFunctionName; if (unrealFunction.GetScriptName(out scriptFunctionName)) { additionalFunctionInfo += ", OriginalName=\"" + unrealFunction.GetName() + "\""; } if (isCollapsedMember) { // The Flags here might not contain too useful information if there is both a get/set function. // Maybe include a second flags var? attributes.Add("UFunctionAsProp(Flags=0x" + ((uint)unrealFunction.FunctionFlags).ToString("X8") + additionalFunctionInfo + ")"); } else { attributes.Add("UFunction(Flags=0x" + ((uint)unrealFunction.FunctionFlags).ToString("X8") + additionalFunctionInfo + ")"); } } } UProperty unrealProperty = field as UProperty; if (unrealProperty != null) { attributes.Add("UProperty(Flags=(PropFlags)0x" + ((ulong)unrealProperty.PropertyFlags).ToString("X16") + ")"); } if (unrealStruct != null) { attributes.Add("UStruct(Flags=0x" + ((uint)unrealStruct.StructFlags).ToString("X8") + ")"); } else if (unrealClass != null) { // Abstract isn't really required but might help with code browsing to know what is abstract // and what isn't. Therefore put it at the start of the attributes list. if (unrealClass.HasAnyClassFlags(EClassFlags.Abstract)) { attributes.Add("Abstract"); } isInterface = unrealClass.IsChildOf <UInterface>(); if (isInterface) { attributes.Add("UInterface(Flags=0x" + ((uint)unrealClass.ClassFlags).ToString("X8") + ")"); } else { attributes.Add("UClass(Flags=(ClassFlags)0x" + ((uint)unrealClass.ClassFlags).ToString("X8") + ")"); } } if (unrealEnum != null) { attributes.Add("UEnum"); } if (unrealEnum != null || unrealClass != null || unrealStruct != null) { bool blueprintType = false; bool blueprintable = false; if (unrealEnum != null) { blueprintType = field.GetBoolMetaData(MDClass.BlueprintType); } else { GetBlueprintability(field as UStruct, out blueprintType, out blueprintable); } if (blueprintType) { attributes.Add(UMeta.GetKey(MDClass.BlueprintType)); } if (unrealClass != null && blueprintable) { attributes.Add(UMeta.GetKey(MDClass.Blueprintable)); } attributes.Add("UMetaPath(\"" + field.GetPathName() + "\"" + (isInterface ? ", InterfaceImpl=typeof(" + GetTypeName(unrealClass, null) + "Impl" + ")" : string.Empty) + ")"); } else { attributes.Add("UMetaPath(\"" + field.GetPathName() + "\")"); } if (attributes.Count > 0) { builder.AppendLine("[" + string.Join(", ", attributes) + "]"); } }
public void ResolveCollapsedMembers() { if (IsInterface) { // Interface shouldn't have any C# properties, leave everything as functions ResolveNameConflicts(); return; } collapsedMembers.Clear(); collapsedMembersByFunction.Clear(); collapsedMembersByProperty.Clear(); var getters = new Dictionary <UFunction, CodeGeneratorSettings.CollapsedMemberSettings>(); var setters = new Dictionary <UFunction, CodeGeneratorSettings.CollapsedMemberSettings>(); // Conflicts aren't resolved at this point. May have multiple functions for a given name. // - If there are conflicts avoid collapsing those functions. Dictionary <string, List <UFunction> > gettersByName = new Dictionary <string, List <UFunction> >(); Dictionary <string, List <UFunction> > settersByName = new Dictionary <string, List <UFunction> >(); foreach (UFunction function in exportableFunctions) { string functionName = codeGenerator.GetFunctionName(function, false); // 1 param either as return value or parameter and no return if (function.NumParms == 1) { UProperty returnProperty = function.GetReturnProperty(); if (returnProperty != null) { // Getter foreach (var collapsedSetting in codeGenerator.Settings.CollapsedMembers) { if (!string.IsNullOrEmpty(collapsedSetting.GetPrefix) && functionName.StartsWith(collapsedSetting.GetPrefix) && (!collapsedSetting.RequiresBool || returnProperty.IsA <UBoolProperty>())) { getters.Add(function, collapsedSetting); string trimmedName = functionName.Substring(collapsedSetting.GetPrefix.Length); if (trimmedName.Length > 0) { List <UFunction> functions; if (!gettersByName.TryGetValue(trimmedName, out functions)) { gettersByName.Add(trimmedName, functions = new List <UFunction>()); } functions.Add(function); } break; } } } else { // Setter UProperty firstParam = function.GetFirstParam(); if (firstParam != null) { foreach (var collapsedSetting in codeGenerator.Settings.CollapsedMembers) { if (!string.IsNullOrEmpty(collapsedSetting.SetPrefix) && functionName.StartsWith(collapsedSetting.SetPrefix) && (!collapsedSetting.RequiresBool || firstParam.IsA <UBoolProperty>())) { setters.Add(function, collapsedSetting); string trimmedName = functionName.Substring(collapsedSetting.GetPrefix.Length); if (trimmedName.Length > 0) { List <UFunction> functions; if (!settersByName.TryGetValue(trimmedName, out functions)) { settersByName.Add(trimmedName, functions = new List <UFunction>()); } functions.Add(function); } break; } } } } } } for (int i = 0; i < 2; i++) { bool isGetter = i == 0; Dictionary <string, List <UFunction> > collection = isGetter ? gettersByName : settersByName; foreach (KeyValuePair <string, List <UFunction> > funcs in collection) { if (funcs.Value.Count != 1) { continue; } string name = funcs.Key; UFunction getter = isGetter ? funcs.Value[0] : null; UFunction setter = !isGetter ? funcs.Value[0] : null; CodeGeneratorSettings.CollapsedMemberSettings settings = null; UProperty paramOrRetValProperty = null; Dictionary <string, List <UFunction> > otherCollection = isGetter ? settersByName : gettersByName; List <UFunction> otherFuncs; if (otherCollection.TryGetValue(funcs.Key, out otherFuncs)) { if (otherFuncs.Count > 1) { // Other function has a conflict continue; } if (isGetter) { setter = otherFuncs[0]; } else { getter = otherFuncs[0]; } } if ((getter != null && collapsedMembersByFunction.ContainsKey(getter)) || (setter != null && collapsedMembersByFunction.ContainsKey(setter))) { continue; } if (getter != null && setter != null) { UProperty returnProperty = getter.GetReturnProperty(); UProperty firstParam = setter.GetFirstParam(); if (returnProperty != null && firstParam != null && !returnProperty.SameType(firstParam)) { // Property type mismatch on Get/Set functions continue; } } if (getter != null) { paramOrRetValProperty = getter.GetReturnProperty(); settings = getters[getter]; } else if (setter != null) { paramOrRetValProperty = setter.GetFirstParam(); settings = setters[setter]; } if (paramOrRetValProperty == null) { continue; } UProperty backingProperty = null; bool backingPropertyExportable = false; foreach (KeyValuePair <UProperty, string> property in exportableProperties) { if (name == codeGenerator.GetMemberName(property.Key, false, property.Value) && property.Key.SameType(paramOrRetValProperty)) { if (backingProperty != null) { // Skip conflicts continue; } backingProperty = property.Key; backingPropertyExportable = true; } } if (backingProperty == null) { foreach (KeyValuePair <UProperty, string> property in nonExportableProperties) { if (name == codeGenerator.GetMemberName(property.Key, false, property.Value) && property.Key.SameType(paramOrRetValProperty)) { if (backingProperty != null) { // Skip conflicts continue; } backingProperty = property.Key; backingPropertyExportable = false; } } } if (getter == null && setter != null) { // SetXXX exists but there isn't a backing property or the backing property isn't // exportable so there wouldn't be any way access the property. Leave the function // as it is rather than creating a setter-only C# property unless the getter is injected. if (backingProperty == null || (!backingPropertyExportable && !settings.InjectNonExportableProperty)) { continue; } if (settings.SetRequiresGet) { continue; } } else if (getter != null && setter == null) { if (settings.GetRequiresSet) { continue; } } if ((getter != null && getter.HasAllFunctionFlags(EFunctionFlags.BlueprintEvent)) || (setter != null && setter.HasAnyFunctionFlags(EFunctionFlags.BlueprintEvent))) { // Skip events as they need a normal method body continue; } string finalName = name; if (!settings.StripPrefix && (getter == null || setter == null)) { if (getter != null) { finalName = settings.GetPrefix + finalName; } else if (setter != null) { finalName = settings.SetPrefix + finalName; } } if (backingProperty != null && backingProperty.HasAnyPropertyFlags(EPropertyFlags.BlueprintReadOnly) && IsCollectionProperty(backingProperty) && setter != null) { // If there is a backing property which is a readonly collection and there is a setter method then // there will be type conflicts. Don't collapse them. continue; } // Some validation on bool properties { UBoolProperty getterReturn = getter == null ? null : getter.GetReturnProperty() as UBoolProperty; UBoolProperty setterParam = setter == null ? null : setter.GetFirstParam() as UBoolProperty; if (getterReturn != null && setterParam != null) { System.Diagnostics.Debug.Assert(getterReturn.ElementSize == setterParam.ElementSize, "Get/Set use different bool size"); } UBoolProperty backingBoolProperty = backingProperty as UBoolProperty; if (backingBoolProperty != null) { if (getter != null) { System.Diagnostics.Debug.Assert(backingBoolProperty.ElementSize == getterReturn.ElementSize, "BackingProperty/Get use different bool size"); } else if (setter != null) { System.Diagnostics.Debug.Assert(backingBoolProperty.ElementSize == setterParam.ElementSize, "BackingProperty/Set use different bool size"); } } } CollapsedMember collapsedMember = new CollapsedMember(settings); collapsedMember.BackingProperty = backingProperty; collapsedMember.IsBackingPropertyExportable = backingPropertyExportable; collapsedMember.Getter = getter; collapsedMember.Setter = setter; collapsedMember.Name = finalName; collapsedMember.Property = paramOrRetValProperty; collapsedMembers.Add(collapsedMember); if (getter != null) { collapsedMembersByFunction.Add(getter, collapsedMember); } if (setter != null) { collapsedMembersByFunction.Add(setter, collapsedMember); } if (backingPropertyExportable) { collapsedMembersByProperty.Add(backingProperty, collapsedMember); } } } ResolveNameConflicts(); }
private void AppendAttribute(CSharpTextBuilder builder, UField field, UnrealModuleInfo module, bool isCollapsedMember) { UnrealModuleType moduleType; UnrealModuleType moduleAssetType; string moduleName = GetModuleName(field, out moduleType, out moduleAssetType); if (string.IsNullOrEmpty(moduleName)) { moduleName = module.Name; } List <string> attributes = new List <string>(); // TODO: Combine all of this into EPropertyType (add some TypeCode into UField?) bool isInterface = false; UEnum unrealEnum = field as UEnum; UClass unrealClass = field as UClass; UScriptStruct unrealStruct = field as UScriptStruct; UFunction unrealFunction = field as UFunction; if (unrealFunction != null) { if (unrealFunction.HasAnyFunctionFlags(EFunctionFlags.Delegate)) { attributes.Add("UDelegate"); } else { if (isCollapsedMember) { // The Flags here might not contain too useful information if there is both a get/set function. // Maybe include a second flags var? attributes.Add("UFunctionAsProp(Flags=0x" + ((uint)unrealFunction.FunctionFlags).ToString("X8") + ")"); } else { attributes.Add("UFunction(Flags=0x" + ((uint)unrealFunction.FunctionFlags).ToString("X8") + ")"); } } } UProperty unrealProperty = field as UProperty; if (unrealProperty != null) { attributes.Add("UProperty(Flags=(PropFlags)0x" + ((ulong)unrealProperty.PropertyFlags).ToString("X16") + ")"); } if (unrealStruct != null) { attributes.Add("UStruct(Flags=0x" + ((uint)unrealStruct.StructFlags).ToString("X8") + ")"); } else if (unrealClass != null) { // Abstract isn't really required but might help with code browsing to know what is abstract // and what isn't. Therefore put it at the start of the attributes list. if (unrealClass.HasAnyClassFlags(EClassFlags.Abstract)) { attributes.Add("Abstract"); } isInterface = unrealClass.IsChildOf <UInterface>(); if (isInterface) { attributes.Add("UInterface(Flags=0x" + ((uint)unrealClass.ClassFlags).ToString("X8") + ")"); } else { // Should we skip "inherit" config name? string configNameStr = string.Empty; if (unrealClass.ClassConfigName != FName.None && !unrealClass.ClassConfigName.ToString().Equals("inherit", StringComparison.InvariantCultureIgnoreCase)) { configNameStr = ", Config=\"" + unrealClass.ClassConfigName + "\""; } attributes.Add("UClass(Flags=(ClassFlags)0x" + ((uint)unrealClass.ClassFlags).ToString("X8") + configNameStr + ")"); } } if (unrealEnum != null) { attributes.Add("UEnum"); } if (unrealEnum != null || unrealClass != null || unrealStruct != null) { bool blueprintType = false; bool blueprintable = false; if (unrealEnum != null) { blueprintType = field.GetBoolMetaData(MDClass.BlueprintType); } else { GetBlueprintability(field as UStruct, out blueprintType, out blueprintable); } if (blueprintType) { attributes.Add(UMeta.GetKey(MDClass.BlueprintType)); } if (unrealClass != null && blueprintable) { attributes.Add(UMeta.GetKey(MDClass.Blueprintable)); } if (isInterface) { } attributes.Add("UMetaPath(\"" + field.GetPathName() + "\", \"" + moduleName + "\", UnrealModuleType." + GetUnrealModuleTypeString(moduleType, moduleAssetType) + (isInterface ? ", InterfaceImpl=typeof(" + GetTypeName(unrealClass, null) + "Impl" + ")" : string.Empty) + ")"); } else { attributes.Add("UMetaPath(\"" + field.GetPathName() + "\")"); } if (attributes.Count > 0) { builder.AppendLine("[" + string.Join(", ", attributes) + "]"); } }
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 void AppendFunctionOffsets(CSharpTextBuilder builder, CSharpTextBuilder offsetsBuilder, UFunction function, bool isGetter, bool isSetter, List <string> namespaces) { bool isInterface = false; UClass owner = function.GetOwnerClass(); if (owner != null && owner.ClassFlags.HasFlag(EClassFlags.Interface)) { isInterface = true; } string functionName = GetFunctionName(function); if (isGetter) { functionName += "_getter"; } else if (isSetter) { functionName += "_setter"; } Dictionary <UProperty, string> paramNames = GetParamNames(function); if (Settings.GenerateIsValidSafeguards) { builder.AppendLine("static bool " + functionName + Settings.VarNames.IsValid + ";"); } if ((function.HasAnyFunctionFlags(EFunctionFlags.BlueprintEvent) && function.GetSuperFunction() == null) || isInterface) { builder.AppendLine("IntPtr " + functionName + Settings.VarNames.InstanceFunctionAddress + ";"); } builder.AppendLine("static IntPtr " + functionName + Settings.VarNames.FunctionAddress + ";"); builder.AppendLine("static int " + functionName + Settings.VarNames.ParamsSize + ";"); foreach (KeyValuePair <UProperty, string> param in paramNames) { UProperty parameter = param.Key; string paramName = param.Value; if (!parameter.HasAnyPropertyFlags(EPropertyFlags.Parm)) { continue; } AppendPropertyOffset(builder, functionName + "_" + paramName, parameter, true, namespaces); } offsetsBuilder.AppendLine(functionName + Settings.VarNames.FunctionAddress + " = " + Names.NativeReflectionCached_GetFunction + "(" + Settings.VarNames.ClassAddress + ", \"" + function.GetName() + "\");"); offsetsBuilder.AppendLine(functionName + Settings.VarNames.ParamsSize + " = " + Names.NativeReflection_GetFunctionParamsSize + "(" + functionName + Settings.VarNames.FunctionAddress + ");"); foreach (KeyValuePair <UProperty, string> param in paramNames) { UProperty parameter = param.Key; string paramName = param.Value; if (!parameter.HasAnyPropertyFlags(EPropertyFlags.Parm)) { continue; } AppendPropertyOffsetNativeTypeLoader(offsetsBuilder, functionName + "_" + paramName, parameter, functionName); } if (Settings.GenerateIsValidSafeguards) { // XXXX_IsValid = param1_IsValid && param2_IsValid && param3_IsValid; string paramsValid = string.Join(" && ", paramNames.Values.Select(x => functionName + "_" + x + Settings.VarNames.IsValid)); if (!string.IsNullOrEmpty(paramsValid)) { paramsValid = " && " + paramsValid; } offsetsBuilder.AppendLine(functionName + Settings.VarNames.IsValid + " = " + functionName + Settings.VarNames.FunctionAddress + " != IntPtr.Zero" + paramsValid + ";"); offsetsBuilder.AppendLine(Names.NativeReflection_LogFunctionIsValid + "(\"" + function.GetPathName() + "\", " + functionName + Settings.VarNames.IsValid + ");"); } }
private void AppendDelegateSignature(UnrealModuleInfo module, CSharpTextBuilder builder, UFunction function, UStruct owner, bool isBlueprintType, List <string> namespaces) { AppendDocComment(builder, function, isBlueprintType); AppendAttribute(builder, function, module); string delegateBaseTypeName = function.HasAnyFunctionFlags(EFunctionFlags.MulticastDelegate) ? Names.FMulticastDelegate : Names.FDelegate; string delegateTypeName = GetTypeNameDelegate(function); builder.AppendLine("public class " + delegateTypeName + " : " + delegateBaseTypeName + "<" + delegateTypeName + "." + Settings.VarNames.DelegateSignature + ">"); builder.OpenBrace(); builder.AppendLine(GetFunctionSignature( module, function, owner, Settings.VarNames.DelegateSignature, "public delegate", false, false, namespaces)); builder.AppendLine(); builder.AppendLine("public override " + Settings.VarNames.DelegateSignature + " " + Names.FDelegateBase_GetInvoker + "()"); builder.OpenBrace(); builder.AppendLine("return " + Settings.VarNames.DelegateInvoker + ";"); builder.CloseBrace(); builder.AppendLine(); string functionName = GetFunctionName(function); Dictionary <UProperty, string> paramNames = GetParamNames(function); // Offsets if (Settings.GenerateIsValidSafeguards) { builder.AppendLine("static bool " + functionName + Settings.VarNames.IsValid + ";"); } builder.AppendLine("static IntPtr " + functionName + Settings.VarNames.FunctionAddress + ";"); builder.AppendLine("static int " + functionName + Settings.VarNames.ParamsSize + ";"); foreach (KeyValuePair <UProperty, string> param in paramNames) { UProperty parameter = param.Key; string paramName = param.Value; if (!parameter.HasAnyPropertyFlags(EPropertyFlags.Parm)) { continue; } AppendPropertyOffset(builder, functionName + "_" + paramName, parameter, true, namespaces); } // Add the native type info initializer to get the offsets builder.AppendLine("static void " + Settings.VarNames.LoadNativeType + "()"); builder.OpenBrace(); builder.AppendLine(functionName + Settings.VarNames.FunctionAddress + " = " + Names.NativeReflection_GetFunction + "(\"" + function.GetPathName() + "\");"); builder.AppendLine(functionName + Settings.VarNames.ParamsSize + " = " + Names.NativeReflection_GetFunctionParamsSize + "(" + functionName + Settings.VarNames.FunctionAddress + ");"); foreach (KeyValuePair <UProperty, string> param in paramNames) { UProperty parameter = param.Key; string paramName = param.Value; if (!parameter.HasAnyPropertyFlags(EPropertyFlags.Parm)) { continue; } AppendPropertyOffsetNativeTypeLoader(builder, functionName + "_" + paramName, parameter, functionName); } if (Settings.GenerateIsValidSafeguards) { // XXXX_IsValid = param1_IsValid && param2_IsValid && param3_IsValid; string paramsValid = string.Join(" && ", paramNames.Values.Select(x => functionName + "_" + x + Settings.VarNames.IsValid)); if (!string.IsNullOrEmpty(paramsValid)) { paramsValid = " && " + paramsValid; } builder.AppendLine(functionName + Settings.VarNames.IsValid + " = " + functionName + Settings.VarNames.FunctionAddress + " != IntPtr.Zero" + paramsValid + ";"); builder.AppendLine(Names.NativeReflection_LogFunctionIsValid + "(\"" + function.GetPathName() + "\", " + functionName + Settings.VarNames.IsValid + ");"); } builder.CloseBrace(); builder.AppendLine(); builder.AppendLine(GetFunctionSignature( module, function, owner, Settings.VarNames.DelegateInvoker, "private", true, false, namespaces)); builder.OpenBrace(); AppendFunctionBody(builder, function, false, false, false, namespaces); builder.CloseBrace(); builder.CloseBrace(); }
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)); }
private bool CanExportFunction(UFunction function, bool isBlueprintType) { UClass ownerClass = function.GetOuter() as UClass; if (ownerClass != null && function.HasAnyFunctionFlags(EFunctionFlags.BlueprintEvent) && function.GetSuperFunction() == null) { UFunction originalFunction; bool isInterfaceImplementation; UClass originalOwner = GetOriginalFunctionOwner(function, out originalFunction, out isInterfaceImplementation); // Let interface implementation functions through as we need them for implementing the interface. if (originalOwner != ownerClass && !isInterfaceImplementation) { // BlueprintEvent function is defined twice in the hierarchy (this should only be possible in // C++. Blueprint will have SuperFunction set). There isn't any logical code to output for this // and Blueprint seems to just access the base-most function anyway. Debug.Assert(function.HasAnyFunctionFlags(EFunctionFlags.Native)); return(false); } } // Make sure we use the GetOriginalFunctionOwner check before ExportAllFunctions as we aren't handling the // "new" keyword properly yet for redefined virtual functions. if (Settings.ExportAllFunctions) { return(true); } // Should we allow deprecated functions and tag them with [Obsolete]? if (function.HasMetaData(MDFunc.DeprecatedFunction)) { return(false); } if (function.GetBoolMetaData(MDFunc.BlueprintInternalUseOnly)) { return(false); } if (function.HasAnyFunctionFlags(EFunctionFlags.Delegate | EFunctionFlags.MulticastDelegate)) { return(true); } if (isBlueprintType && function.HasAnyFunctionFlags(EFunctionFlags.BlueprintEvent)) { // Skip events such as input events which can be implemented many times // which are hard to generate code for // "InpAxisEvt_LookUpRate_K2Node_InputAxisEvent_62" // // NOTE: This check may not be enough, we may need to check the UEdGraph nodes // for additional information bool isNativeEvent = function.HasAnyFunctionFlags(EFunctionFlags.Event); bool isCallable = function.HasAnyFunctionFlags(EFunctionFlags.BlueprintCallable); bool hasSuperFunc = function.GetSuperFunction() != null; // Check bIsNativeEvent if we want events such as ReceiveBeginPlay / ReceiveHit if (/*!isNativeEvent && */ !isCallable) { return(false); } } // Maybe check metadata "BlueprintProtected" for true? In blueprint how do the // dropdowns private/protected/public impact the UFunction? // Functions don't need to be marked as FUNC_Public to be visible by blueprint? // The FUNC_BlueprintCallable and other all that matters? return(function.HasAnyFunctionFlags(EFunctionFlags.BlueprintCallable | EFunctionFlags.BlueprintEvent | EFunctionFlags.BlueprintPure));// && //function.HasAnyFunctionFlags(EFunctionFlags.Public | EFunctionFlags.Protected); }