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 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) + "]"); } }
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 + ");"); } }