public string ResolveNameConflict(UField field, string name) { UFunction function = field as UFunction; if (function != null) { // Functions are a special case and must use the base-most function for name resolving. // See above for more info on this. UFunction originalFunction; UClass originalOwner = codeGenerator.GetOriginalFunctionOwner(function, out originalFunction); if (originalOwner != Class) { StructInfo originalOwnerStructInfo = codeGenerator.GetStructInfo(originalOwner); return(originalOwnerStructInfo.ResolveNameConflict(originalFunction, name)); } } string resolvedName; if (conflictInfo.ResolvedName.TryGetValue(field, out resolvedName)) { return(resolvedName); } return(name); }
/// <summary> /// Searches the class hierarchy to find the original owner of the given function (by function name) /// </summary> private UClass GetOriginalFunctionOwner(UFunction function) { UFunction originalFunction; bool isInterfaceImplementation; return(GetOriginalFunctionOwner(function, out originalFunction, out isInterfaceImplementation)); }
private void AppendSetter(CSharpTextBuilder builder, string propertyName, UField setter, List <string> namespaces) { builder.AppendLine("set"); builder.OpenBrace(); UFunction function = setter as UFunction; if (function != null) { AppendFunctionBody(builder, function, false, true, false, namespaces); } else { if (Settings.CheckObjectDestroyed) { builder.AppendLine(Names.UObject_CheckDestroyed + "();"); } if (Settings.GenerateIsValidSafeguards) { builder.AppendLine("if (!" + propertyName + Settings.VarNames.IsValid + ")"); builder.OpenBrace(); builder.AppendLine(Names.NativeReflection_LogInvalidPropertyAccessed + "(\"" + setter.GetPathName() + "\");"); builder.AppendLine("return;"); builder.CloseBrace(); } AppendPropertyToNative(builder, setter as UProperty, propertyName, Names.UObject_Address, "this", "value", false, namespaces); } builder.CloseBrace(); }
private static object DynamicInvokeInternal(UClass unrealClass, UObject obj, string functionName, params object[] parameters) { UFunction function = obj.GetClass().FindFunctionByName(new FName(functionName)); if (function == null) { return(null); } return(function.DynamicInvoke(obj, parameters)); }
/// <summary> /// Reimplementation of UFunction::IsSignatureCompatibleWith for debugging purposes /// (the native function with engine symbols debugs badly) /// </summary> internal bool InternalIsSignatureCompatibleWith(UFunction otherFunction, EPropertyFlags ignoreFlags) { // Early out if they're exactly the same function if (this == otherFunction) { return(true); } // Run thru the parameter property chains to compare each property TFieldIterator <UProperty> iteratorA = new TFieldIterator <UProperty>(this); TFieldIterator <UProperty> iteratorB = new TFieldIterator <UProperty>(otherFunction); while (iteratorA.Current != null && (iteratorA.Current.PropertyFlags.HasFlag(EPropertyFlags.Parm))) { if (iteratorB.Current != null && (iteratorB.Current.PropertyFlags.HasFlag(EPropertyFlags.Parm))) { // Compare the two properties to make sure their types are identical // Note: currently this requires both to be strictly identical and wouldn't allow functions that differ only by how derived a class is, // which might be desirable when binding delegates, assuming there is directionality in the SignatureIsCompatibleWith call UProperty propA = iteratorA.Current; UProperty propB = iteratorB.Current; EPropertyFlags flags1 = propA.PropertyFlags; EPropertyFlags flags2 = propB.PropertyFlags; if (!ArePropertiesTheSame(propA, propB, false)) { // Type mismatch between an argument of A and B return(false); } // Check the flags as well EPropertyFlags propertyMash = propA.PropertyFlags ^ propB.PropertyFlags; if ((propertyMash & ~ignoreFlags) != 0) { return(false); } } else { // B ran out of arguments before A did return(false); } iteratorA.MoveNext(); iteratorB.MoveNext(); } // They matched all the way thru A's properties, but it could still be a mismatch if B has remaining parameters return(!(iteratorB.Current != null && (iteratorB.Current.PropertyFlags.HasFlag(EPropertyFlags.Parm)))); }
/// <summary> /// Searches the class hierarchy to find the original owner of the given function (by function name) /// </summary> private UClass GetOriginalFunctionOwner(UFunction function, out UFunction originalFunction, out bool isInterfaceImplementation) { KeyValuePair <UFunction, bool> funcInfo; if (lazyOriginalFunctionCache.TryGetValue(function, out funcInfo)) { originalFunction = funcInfo.Key; isInterfaceImplementation = funcInfo.Value; return(originalFunction.GetOwnerClass()); } UClass result = GetOriginalFunctionOwnerInternal(function, out originalFunction, out isInterfaceImplementation); lazyOriginalFunctionCache.Add(function, new KeyValuePair <UFunction, bool>(originalFunction, isInterfaceImplementation)); return(result); }
/// <summary> /// Sets function parameters which are tagged as "out" to default values to satisfy the compiler. This will insert /// a return statement if there is a return value. /// </summary> private void AppendFunctionBodyDefaultValues(CSharpTextBuilder builder, UFunction function, UProperty blueprintReturnProperty, bool asElseStatement, bool insertReturn, Dictionary <UProperty, string> paramNames, List <string> namespaces) { bool hasElse = false; string returnStr = null; foreach (KeyValuePair <UProperty, string> param in paramNames) { UProperty parameter = param.Key; string paramName = param.Value; if (parameter.HasAnyPropertyFlags(EPropertyFlags.ReturnParm) || parameter == blueprintReturnProperty) { returnStr = "return " + GetPropertyMarshalerDefaultValue(parameter, namespaces) + ";"; } else if (parameter.HasAnyPropertyFlags(EPropertyFlags.OutParm) && !parameter.HasAnyPropertyFlags(EPropertyFlags.ReferenceParm)) { if (asElseStatement && !hasElse) { hasElse = true; builder.AppendLine("else"); builder.OpenBrace(); } builder.AppendLine(paramName + " = " + GetPropertyMarshalerDefaultValue(parameter, namespaces) + ";"); } } if (!string.IsNullOrEmpty(returnStr)) { if (asElseStatement && !hasElse) { hasElse = true; builder.AppendLine("else"); builder.OpenBrace(); } builder.AppendLine(returnStr); } else if (insertReturn) { builder.AppendLine("return;"); } if (hasElse) { builder.CloseBrace(); } }
/// <summary> /// Disassemble all of the script code in a single structure. /// </summary> /// <param name="source">The structure to disassemble.</param> public void DisassembleStructure(UFunction source) { script = source.Script; indents = string.Empty; if (script != null) { scriptIndex = 0; while (scriptIndex < script.Length) { output.AppendLine("Label_0x" + scriptIndex.ToString("X8") + ":"); AddIndent(); SerializeExpr(); DropIndent(); } } }
public UFunction[] GetFunctions() { UFunction[] result = new UFunction[Count]; if (IsNative) { for (int i = 0; i < result.Length; i++) { result[i] = (*scriptDelegatePtr)[i].GetFunction(); } } else { for (int i = 0; i < result.Length; i++) { result[i] = managedScriptDelegate[i].GetFunction(); } } return(result); }
/// <summary> /// This will check for conflicts when finding interface functions. If there is a conflict /// the code output is likely to be undesirable. /// </summary> private void ValidateNoInterfaceFunctionConflict(UClass unrealClass, UFunction function, UClass skipInterface, bool skipSelf) { FName functionName = function.GetFName(); UFunction conflictFunction = null; foreach (FImplementedInterface implementedInterface in unrealClass.Interfaces) { UClass interfaceClass = implementedInterface.InterfaceClass; if (interfaceClass != null && interfaceClass != skipInterface) { if ((conflictFunction = interfaceClass.FindFunctionByName(functionName, true)) != null) { break; } } } if (conflictFunction == null && !skipSelf) { conflictFunction = unrealClass.FindFunctionByName(functionName, false); } if (conflictFunction == null) { UClass parentClass = unrealClass.GetSuperClass(); if (parentClass != null) { // Search the rest of the hierarchy conflictFunction = parentClass.FindFunctionByName(functionName, true); } } if (conflictFunction != null) { string warning = "Function redefined in hierarchy where interfaces are used. This is likely going to produce " + "unexpected results and should be avoided where possible. ImplementedInClass: '" + unrealClass.GetPathName() + "' InterfaceFunc: '" + function.GetPathName() + "' ConflictFunc: '" + conflictFunction.GetPathName() + "'"; FMessage.Log(ELogVerbosity.Warning, warning, "USharp-CodeGenerator"); } }
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 AddMember(UField field, string name, bool isResolvedName) { CollapsedMember collapsedMember = null; UProperty property = field as UProperty; if (property != null) { structInfo.collapsedMembersByProperty.TryGetValue(property, out collapsedMember); } UFunction function = field as UFunction; if (function != null) { structInfo.collapsedMembersByFunction.TryGetValue(function, out collapsedMember); } if (collapsedMember != null) { if (isResolvedName) { collapsedMember.ResolvedName = name; } else { name = collapsedMember.Name; } } NameConflictFieldInfo fieldInfo; if (!MembersByName.TryGetValue(name, out fieldInfo)) { MembersByName.Add(name, fieldInfo = new NameConflictFieldInfo(name)); } fieldInfo.AddField(field, collapsedMember); }
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 { 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) + "]"); } }
/// <summary> /// Reimplementation of UFunction::IsSignatureCompatibleWith for debugging purposes /// (the native function with engine symbols debugs badly) /// </summary> internal bool InternalIsSignatureCompatibleWith(UFunction otherFunction) { return(InternalIsSignatureCompatibleWith(otherFunction, GetDefaultIgnoredSignatureCompatibilityFlags())); }
/// <summary> /// Determines if two functions have an identical signature (note: currently doesn't allow /// matches with class parameters that differ only in how derived they are; there is no /// directionality to the call) /// </summary> /// <param name="otherFunction">Function to compare this function against.</param> /// <param name="ignoreFlags">Custom flags to ignore when comparing parameters between the functions.</param> /// <returns>true if function signatures are compatible.</returns> public bool IsSignatureCompatibleWith(UFunction otherFunction, EFunctionFlags ignoreFlags) { return(Native_UFunction.IsSignatureCompatibleWithFlags(Address, otherFunction == null ? IntPtr.Zero : otherFunction.Address, ignoreFlags)); }
/// <summary> /// Determines if two functions have an identical signature (note: currently doesn't allow /// matches with class parameters that differ only in how derived they are; there is no /// directionality to the call) /// </summary> /// <param name="otherFunction">Function to compare this function against.</param> /// <returns>true if function signatures are compatible.</returns> public bool IsSignatureCompatibleWith(UFunction otherFunction) { return(Native_UFunction.IsSignatureCompatibleWith(Address, otherFunction == null ? IntPtr.Zero : otherFunction.Address)); }
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)); }
// Move this somewhere else? Where would this be more appropriate? public static Type GetTypeFromProperty(UProperty prop) { if (prop == null) { return(null); } switch (prop.PropertyType) { case EPropertyType.Bool: return(typeof(bool)); case EPropertyType.Int8: return(typeof(sbyte)); case EPropertyType.Byte: return(typeof(byte)); case EPropertyType.Int16: return(typeof(short)); case EPropertyType.UInt16: return(typeof(ushort)); case EPropertyType.Int: return(typeof(int)); case EPropertyType.UInt32: return(typeof(uint)); case EPropertyType.Int64: return(typeof(long)); case EPropertyType.UInt64: return(typeof(ulong)); case EPropertyType.Float: return(typeof(float)); case EPropertyType.Double: return(typeof(double)); case EPropertyType.Enum: { UEnum unrealEnum = (prop as UEnumProperty).GetEnum(); if (unrealEnum == null) { return(null); } Type enumType; ManagedUnrealModuleInfo.AllKnownUnrealTypes.TryGetValue(unrealEnum.GetPathName(), out enumType); return(enumType); } case EPropertyType.Str: return(typeof(string)); case EPropertyType.Name: return(typeof(FName)); case EPropertyType.Text: return(typeof(FText)); case EPropertyType.Interface: { UClass unrealClassInterface = (prop as UInterfaceProperty).InterfaceClass; if (unrealClassInterface == null) { return(null); } Type interfaceType; ManagedUnrealModuleInfo.AllKnownUnrealTypes.TryGetValue(unrealClassInterface.GetPathName(), out interfaceType); return(interfaceType); } case EPropertyType.Struct: { UScriptStruct unrealStruct = (prop as UStructProperty).Struct; if (unrealStruct == null) { return(null); } Type structType; ManagedUnrealModuleInfo.AllKnownUnrealTypes.TryGetValue(unrealStruct.GetPathName(), out structType); return(structType); } case EPropertyType.Class: case EPropertyType.Object: case EPropertyType.LazyObject: case EPropertyType.WeakObject: case EPropertyType.SoftClass: case EPropertyType.SoftObject: { UClass objectClass = (prop as UObjectPropertyBase).PropertyClass; switch (prop.PropertyType) { case EPropertyType.Class: objectClass = (prop as UClassProperty).MetaClass; break; case EPropertyType.SoftClass: objectClass = (prop as USoftClassProperty).MetaClass; break; } Type type = null; if (objectClass != null) { // Could use UClass.GetType but using AllKnownUnrealTypes for slightly more coverage // UClass.GetType(objectClass) ManagedUnrealModuleInfo.AllKnownUnrealTypes.TryGetValue(objectClass.GetPathName(), out type); } if (type == null) { //classType = typeof(UObject);// Fall back to UObject? Return null? return(null); } switch (prop.PropertyType) { case EPropertyType.Class: return(typeof(TSubclassOf <>).MakeGenericType(type)); case EPropertyType.LazyObject: return(typeof(TLazyObject <>).MakeGenericType(type)); case EPropertyType.WeakObject: return(typeof(TWeakObject <>).MakeGenericType(type)); case EPropertyType.SoftClass: return(typeof(TSoftClass <>).MakeGenericType(type)); case EPropertyType.SoftObject: return(typeof(TSoftObject <>).MakeGenericType(type)); case EPropertyType.Object: return(type); } return(type); } case EPropertyType.Delegate: case EPropertyType.MulticastDelegate: Type delegateType = null; UFunction signatureFunc = null; if (prop.PropertyType == EPropertyType.Delegate) { signatureFunc = (prop as UDelegateProperty).SignatureFunction; } else if (prop.PropertyType == EPropertyType.MulticastDelegate) { signatureFunc = (prop as UMulticastDelegateProperty).SignatureFunction; } if (signatureFunc != null) { if (ManagedUnrealModuleInfo.AllKnownUnrealTypes.TryGetValue(signatureFunc.GetPathName(), out delegateType)) { if (prop.PropertyType == EPropertyType.Delegate) { if (!delegateType.IsSameOrSubclassOfGeneric(typeof(FDelegate <>))) { delegateType = null; } } else if (prop.PropertyType == EPropertyType.MulticastDelegate) { if (!delegateType.IsSameOrSubclassOfGeneric(typeof(FMulticastDelegate <>))) { delegateType = null; } } } } return(delegateType); case EPropertyType.Array: { UArrayProperty arrayProp = prop as UArrayProperty; Type innerType = GetTypeFromProperty(arrayProp.Inner); if (innerType != null) { // Possibly handle IReadOnlyList? return(typeof(IList <>).MakeGenericType(innerType)); } return(null); } case EPropertyType.Set: { USetProperty setProp = prop as USetProperty; Type innerType = GetTypeFromProperty(setProp.ElementProp); if (innerType != null) { return(typeof(ISet <>).MakeGenericType(innerType)); } return(null); } case EPropertyType.Map: { UMapProperty mapProp = prop as UMapProperty; Type keyType = GetTypeFromProperty(mapProp.KeyProp); Type valueType = GetTypeFromProperty(mapProp.ValueProp); if (keyType != null && valueType != null) { // Possibly handle IReadOnlyDictionary? return(typeof(IDictionary <,>).MakeGenericType(keyType, valueType)); } return(null); } } return(null); }
private void UpdateAvailableTypes(UField field) { if (field == null) { return; } System.Diagnostics.Debug.Assert(!field.IsA <UProperty>(), "Shouldn't have UProperty here"); if (field.IsA <UStruct>() || field.IsA <UEnum>()) { bool isNewElement = availableTypes.Add(field); if (!isNewElement) { return; } } if (Settings.ExportMode != CodeGeneratorSettings.CodeExportMode.Referenced) { return; } // Get all of the references from this struct UStruct unrealStruct = field as UStruct; if (unrealStruct != null) { bool isBlueprintType = unrealStruct.IsA <UUserDefinedStruct>() || unrealStruct.IsA <UBlueprintGeneratedClass>(); // Get struct references from parent class chain UStruct parentStruct = unrealStruct.GetSuperStruct(); while (parentStruct != null) { UpdateAvailableTypes(parentStruct); parentStruct = parentStruct.GetSuperStruct(); } // Get references from interfaces UClass unrealClass = field as UClass; if (unrealClass != null) { foreach (FImplementedInterface implementedInterface in unrealClass.Interfaces) { UpdateAvailableTypes(implementedInterface.InterfaceClass); } } // Get struct references from members foreach (UProperty property in unrealStruct.GetFields <UProperty>(false)) { if (CanExportProperty(property, unrealStruct, isBlueprintType)) { UpdateAvailableTypesProp(property); } } // Get struct references from function params (and return type) foreach (UFunction unrealFunction in unrealStruct.GetFields <UFunction>(false)) { if (CanExportFunction(unrealFunction, isBlueprintType)) { foreach (UProperty parameter in unrealFunction.GetFields <UProperty>()) { UpdateAvailableTypesProp(parameter); } } } } // This should be for global functions only (delegates) UFunction function = field as UFunction; if (function != null) { if (CanExportFunction(function, false)) { UStruct functionOwner = function.GetOuter() as UStruct; if (functionOwner != null) { UpdateAvailableTypes(functionOwner); } foreach (UProperty parameter in function.GetFields <UProperty>()) { UpdateAvailableTypesProp(parameter); } } } }
private void AppendGetterSetterOffsets(CSharpTextBuilder builder, CSharpTextBuilder offsetsBuilder, string propertyName, UProperty property, List <string> namespaces, UFunction getter = null, UFunction setter = null) { if (getter != null) { AppendFunctionOffsets(builder, offsetsBuilder, getter, true, false, namespaces); } if (setter != null) { AppendFunctionOffsets(builder, offsetsBuilder, setter, false, true, namespaces); } if (property != null) { AppendPropertyOffset(builder, propertyName, property, false, namespaces); AppendPropertyOffsetNativeTypeLoader(offsetsBuilder, propertyName, property, null); } }
/// <summary> /// Gets the UClass for the given type /// </summary> /// <param name="type">The type</param> /// <returns>The UClass for the given type</returns> public static UClass GetClass(Type type) { UClass result = null; if (classes.TryGetValue(type, out result)) { return(result); } if (type.IsEnum || type.IsValueType || typeof(IDelegateBase).IsAssignableFrom(type)) { // Find the top-most UClass (UUserDefinedEnum, UEnum, UUserDefinedStruct, UUserStruct, etc) // NOTE: This wont contain any useful information about the actual type itself IntPtr address = IntPtr.Zero; if (type.IsEnum) { address = UEnum.GetEnumAddress(type); } else if (type.IsValueType) { address = UScriptStruct.GetStructAddress(type); } else { address = UFunction.GetDelegateSignatureAddress(type); } if (address != IntPtr.Zero) { return(GetClass(address)); } return(null); } if (!type.IsSameOrSubclassOf(typeof(UObject)) && (!type.IsInterface || !typeof(IInterface).IsAssignableFrom(type)) || type == typeof(IInterface)) { return(null); } if (seenClasses.Contains(type)) { // Note: GetModuleCount uses a lock // TODO: Find some multicast delegate which is called when a module is loaded or a new class type is created. // - FModuleManager::Get().OnProcessLoadedObjectsCallback if (FModuleManager.Get().GetModuleCount() != lastModuleCount) { seenClasses.Clear(); } else { return(null); } } if (!seenClasses.Contains(type)) { seenClasses.Add(type); UMetaPathAttribute pathAttribute; if (UnrealTypes.Native.TryGetValue(type, out pathAttribute)) { IntPtr classAddress = GetClassAddress(pathAttribute.Path); if (classAddress == IntPtr.Zero) { // Fallback if this class isn't loaded yet. TODO: Check if this is the correct method to call. classAddress = NativeReflection.LoadObject(Classes.UClass, IntPtr.Zero, pathAttribute.Path); } if (classAddress != IntPtr.Zero) { UClass unrealClass = GCHelper.Find <UClass>(classAddress); if (unrealClass != null) { classesByAddress[classAddress] = type; classes[type] = unrealClass; return(unrealClass); } } } } return(null); }
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 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 static object DynamicInvokeInternal(UClass unrealClass, UObject obj, string functionName, params object[] parameters) { UFunction func = obj.GetClass().FindFunctionByName(new FName(functionName)); if (func == null) { return(null); } if (parameters == null) { parameters = new object[0]; } bool validParams = true; Dictionary <UProperty, Delegate> fromNativeParams = new Dictionary <UProperty, Delegate>(); Dictionary <UProperty, Delegate> toNativeParams = new Dictionary <UProperty, Delegate>(); UProperty returnValueProp = null; List <UProperty> paramProps = new List <UProperty>(); foreach (UProperty prop in func.GetProperties <UProperty>()) { if (prop.HasAnyPropertyFlags(EPropertyFlags.Parm)) { if (prop.HasAnyPropertyFlags(EPropertyFlags.ReturnParm)) { returnValueProp = prop; } else { paramProps.Add(prop); } Type paramType = UProperty.GetTypeFromProperty(prop); if (paramType == null) { validParams = false; break; } Delegate fromNative = MarshalingDelegateResolverSlow.GetFromNative(paramType); Delegate toNative = MarshalingDelegateResolverSlow.GetToNative(paramType); if (fromNative == null || toNative == null) { validParams = false; break; } fromNativeParams.Add(prop, fromNative); toNativeParams.Add(prop, toNative); } } if (parameters.Length != paramProps.Count) { validParams = false; } if (!validParams) { return(null); } // Sort the parameters by offset, this is assumingly the correct thing to do? // - Otherwise we need to take the param names into this function. Or just not sort at all? paramProps.Sort((x, y) => x.GetOffset_ForUFunction().CompareTo(y.GetOffset_ForUFunction())); object result = null; unsafe { int paramsSize = func.ParmsSize; byte * paramsBufferAllocation = stackalloc byte[func.ParmsSize]; IntPtr paramsBuffer = new IntPtr(paramsBufferAllocation); FMemory.Memzero(paramsBuffer, paramsSize); // Initialize default values for all parameters foreach (UProperty prop in func.GetProperties <UProperty>()) { if (prop.HasAnyPropertyFlags(EPropertyFlags.Parm)) { Native.Native_UProperty.InitializeValue_InContainer(prop.Address, paramsBuffer); } } // Copy the managed parameters to the buffer for (int i = 0; i < parameters.Length; i++) { UProperty paramProp = paramProps[i]; object paramValue = parameters[i]; if (paramValue != null && (!paramProp.HasAnyPropertyFlags(EPropertyFlags.OutParm) || paramProp.HasAnyPropertyFlags(EPropertyFlags.ReferenceParm))) { toNativeParams[paramProp].DynamicInvoke( paramsBuffer + paramProp.GetOffset_ForUFunction(), (int)0, paramProp.Address, paramValue); } } // Invoke the function NativeReflection.InvokeFunction(obj.Address, func.Address, paramsBuffer, paramsSize); // Copy parameters / return value from the buffer for (int i = 0; i < parameters.Length; i++) { UProperty paramProp = paramProps[i]; if (paramProp.HasAnyPropertyFlags(EPropertyFlags.OutParm)) { parameters[i] = fromNativeParams[paramProp].DynamicInvoke( paramsBuffer + paramProp.GetOffset_ForUFunction(), (int)0, paramProp.Address); } } if (returnValueProp != null) { result = fromNativeParams[returnValueProp].DynamicInvoke( paramsBuffer + returnValueProp.GetOffset_ForUFunction(), (int)0, returnValueProp.Address); } // Destroy the memory for all of the parameters foreach (UProperty prop in func.GetProperties <UProperty>()) { if (prop.HasAnyPropertyFlags(EPropertyFlags.Parm)) { Native.Native_UProperty.DestroyValue_InContainer(prop.Address, paramsBuffer); } } } return(result); }
/// <summary> /// Add a function to the function map /// </summary> /// <param name="function"></param> public void AddFunctionToFunctionMap(UFunction function, FName funcName) { Native_UClass.AddFunctionToFunctionMap(Address, function == null ? IntPtr.Zero : function.Address, ref funcName); }
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 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(); }
public bool IsCollapsedFunction(UFunction function) { return(collapsedMembersByFunction.ContainsKey(function)); }
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); } }