| @ -0,0 +1,22 @@ | ||||
| namespace UnrealSharpWeaver.MetaData; | ||||
|  | ||||
| public class ApiMetaData | ||||
| { | ||||
|     public ApiMetaData(string assemblyName) | ||||
|     { | ||||
|         AssemblyName = assemblyName; | ||||
|         ClassMetaData = new List<ClassMetaData>(); | ||||
|         StructMetaData = new List<StructMetaData>(); | ||||
|         EnumMetaData = new List<EnumMetaData>(); | ||||
|         InterfacesMetaData = new List<InterfaceMetaData>(); | ||||
|         DelegateMetaData = new List<DelegateMetaData>(); | ||||
|     } | ||||
|  | ||||
|     public List<ClassMetaData> ClassMetaData { get; set; }   | ||||
|     public List<StructMetaData> StructMetaData { get; set; } | ||||
|     public List<EnumMetaData> EnumMetaData { get; set; } | ||||
|     public List<InterfaceMetaData> InterfacesMetaData { get; set; } | ||||
|     public List<DelegateMetaData> DelegateMetaData { get; set; } | ||||
|      | ||||
|     public string AssemblyName { get; set; } | ||||
| } | ||||
| @ -0,0 +1,282 @@ | ||||
| using System.Globalization; | ||||
| using Mono.Cecil; | ||||
| using Mono.Cecil.Cil; | ||||
| using Mono.Collections.Generic; | ||||
| using UnrealSharpWeaver.Utilities; | ||||
|  | ||||
| namespace UnrealSharpWeaver.MetaData; | ||||
|  | ||||
| public class BaseMetaData | ||||
| { | ||||
|     public string Name { get; set; } | ||||
|     public Dictionary<string, string> MetaData { get; set; } | ||||
|  | ||||
|     // Non-serialized for JSON | ||||
|     public readonly string AttributeName; | ||||
|     public readonly IMemberDefinition MemberDefinition; | ||||
|     public readonly CustomAttribute? BaseAttribute; | ||||
|     public readonly string SourceName; | ||||
|     // End non-serialized | ||||
|  | ||||
|     public BaseMetaData(MemberReference member, string attributeName) | ||||
|     { | ||||
|         MemberDefinition = member.Resolve(); | ||||
|         SourceName = MemberDefinition.Name; | ||||
|         Name = MemberDefinition.GetEngineName(); | ||||
|  | ||||
|         AttributeName = attributeName; | ||||
|         MetaData = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); | ||||
|         BaseAttribute = MemberDefinition.CustomAttributes.FindAttributeByType(WeaverImporter.UnrealSharpAttributesNamespace, AttributeName)!; | ||||
|  | ||||
|         AddMetaData();          // Add any [UMetaData("key", "value")] attributes (general metadata attribute to allow support of any engine tag) | ||||
|         AddMetaTagsNamespace(); // Add all named attributes in the UnrealSharp.Attributes.MetaTags namespace | ||||
|         AddBaseAttributes();    // Add fields from base attribute e.g. [UClass | UFunction | UEnum | UProperty | UStruct] | ||||
|         AddDefaultCategory();   // Add Category="Default" if no category yet added | ||||
|         AddBlueprintAccess();   // Add default Blueprint access if not already added | ||||
|     } | ||||
|  | ||||
|     public void TryAddMetaData(string key, string value = "") | ||||
|     { | ||||
|         if (MetaData.TryAdd(key, value)) | ||||
|         { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         MetaData[key] = value; | ||||
|     } | ||||
|  | ||||
|     public void TryAddMetaData(string key, bool value) | ||||
|     { | ||||
|         TryAddMetaData(key, value ? "true" : "false"); | ||||
|     } | ||||
|  | ||||
|     public void TryAddMetaData(string key, int value) | ||||
|     { | ||||
|         TryAddMetaData(key, value.ToString()); | ||||
|     } | ||||
|  | ||||
|     public void TryAddMetaData(string key, ulong value) | ||||
|     { | ||||
|         TryAddMetaData(key, value.ToString()); | ||||
|     } | ||||
|  | ||||
|     public void TryAddMetaData(string key, float value) | ||||
|     { | ||||
|         TryAddMetaData(key, value.ToString()); | ||||
|     } | ||||
|  | ||||
|     public void TryAddMetaData(string key, double value) | ||||
|     { | ||||
|         TryAddMetaData(key, value.ToString()); | ||||
|     } | ||||
|  | ||||
|     public void TryAddMetaData(string key, object value) | ||||
|     { | ||||
|         TryAddMetaData(key, value?.ToString() ?? ""); | ||||
|     } | ||||
|  | ||||
|     public void AddDefaultCategory() | ||||
|     { | ||||
|         if (!MetaData.ContainsKey("Category")) | ||||
|         { | ||||
|             TryAddMetaData("Category", "Default"); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public void AddBlueprintAccess() | ||||
|     { | ||||
|         if (MetaData.ContainsKey("NotBlueprintType")) | ||||
|         { | ||||
|             return; | ||||
|         } | ||||
|          | ||||
|         TryAddMetaData("BlueprintType", "true"); | ||||
|         TryAddMetaData("IsBlueprintBase", "true"); | ||||
|     } | ||||
|  | ||||
|     public static ulong GetFlags(IEnumerable<CustomAttribute> customAttributes, string flagsAttributeName) | ||||
|     { | ||||
|         CustomAttribute? flagsAttribute = customAttributes.FindAttributeByType(WeaverImporter.UnrealSharpNamespace + ".Attributes", flagsAttributeName); | ||||
|         return flagsAttribute == null ? 0 : GetFlags(flagsAttribute); | ||||
|     } | ||||
|  | ||||
|     public static ulong GetFlags(CustomAttribute flagsAttribute) | ||||
|     { | ||||
|         return (ulong) flagsAttribute.ConstructorArguments[0].Value; | ||||
|     } | ||||
|  | ||||
|     public static ulong ExtractBoolAsFlags(TypeDefinition attributeType, CustomAttributeNamedArgument namedArg, string flagsAttributeName) | ||||
|     { | ||||
|         var arg = namedArg.Argument; | ||||
|  | ||||
|         if (!(bool)arg.Value) | ||||
|         { | ||||
|             return 0; | ||||
|         } | ||||
|  | ||||
|         // Find the property definition for this argument to resolve the true value to the desired flags map. | ||||
|         var properties = (from prop in attributeType.Properties where prop.Name == namedArg.Name select prop).ToArray(); | ||||
|         TypeProcessors.ConstructorBuilder.VerifySingleResult(properties, attributeType, "attribute property " + namedArg.Name); | ||||
|         return GetFlags(properties[0].CustomAttributes, flagsAttributeName); | ||||
|     } | ||||
|  | ||||
|     public static ulong ExtractStringAsFlags(TypeDefinition attributeType, CustomAttributeNamedArgument namedArg, string flagsAttributeName) | ||||
|     { | ||||
|         var arg = namedArg.Argument; | ||||
|         var argValue = (string) arg.Value; | ||||
|  | ||||
|         if (argValue is not { Length: > 0 }) | ||||
|         { | ||||
|             return 0; | ||||
|         } | ||||
|  | ||||
|         PropertyDefinition? foundProperty = attributeType.FindPropertyByName(namedArg.Name); | ||||
|  | ||||
|         if (foundProperty == null) | ||||
|         { | ||||
|             return 0; | ||||
|         } | ||||
|  | ||||
|         TypeProcessors.ConstructorBuilder.VerifySingleResult([foundProperty], attributeType, "attribute property " + namedArg.Name); | ||||
|         return GetFlags(foundProperty.CustomAttributes, flagsAttributeName); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     public static ulong GetFlags(IMemberDefinition member, string flagsAttributeName) | ||||
|     { | ||||
|         SequencePoint? sequencePoint = ErrorEmitter.GetSequencePointFromMemberDefinition(member); | ||||
|         Collection<CustomAttribute>? customAttributes = member.CustomAttributes; | ||||
|          | ||||
|         ulong flags = 0; | ||||
|  | ||||
|         foreach (CustomAttribute attribute in customAttributes) | ||||
|         { | ||||
|             TypeDefinition? attributeClass = attribute.AttributeType.Resolve(); | ||||
|  | ||||
|             if (attributeClass == null) | ||||
|             { | ||||
|                 continue; | ||||
|             } | ||||
|  | ||||
|             CustomAttribute? flagsMap = attributeClass.CustomAttributes.FindAttributeByType(WeaverImporter.UnrealSharpAttributesNamespace, flagsAttributeName); | ||||
|  | ||||
|             if (flagsMap == null) | ||||
|             { | ||||
|                 continue; | ||||
|             } | ||||
|  | ||||
|             flags |= GetFlags(flagsMap); | ||||
|  | ||||
|             if (attribute.HasConstructorArguments) | ||||
|             { | ||||
|                 foreach (CustomAttributeArgument arg in attribute.ConstructorArguments) | ||||
|                 { | ||||
|                     flags |= Convert.ToUInt64(arg.Value); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             if (!attribute.HasProperties) | ||||
|             { | ||||
|                 continue; | ||||
|             } | ||||
|  | ||||
|             foreach (CustomAttributeNamedArgument arg in attribute.Properties) | ||||
|             { | ||||
|                 TypeDefinition argType = arg.Argument.Type.Resolve(); | ||||
|  | ||||
|                 if (argType.IsValueType && argType.Namespace == "System" && argType.Name == "Boolean") | ||||
|                 { | ||||
|                     flags |= ExtractBoolAsFlags(attributeClass, arg, flagsAttributeName); | ||||
|                 } | ||||
|                 else if (argType.Namespace == "System" && argType.Name == "String") | ||||
|                 { | ||||
|                     flags |= ExtractStringAsFlags(attributeClass, arg, flagsAttributeName); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     throw new InvalidAttributeException(attributeClass, sequencePoint, $"{argType.FullName} is not supported as an attribute property type."); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return flags; | ||||
|     } | ||||
|  | ||||
|     private void AddMetaData() | ||||
|     { | ||||
|         AddMetaData(MemberDefinition); | ||||
|     } | ||||
|  | ||||
|     protected void AddMetaData(ICustomAttributeProvider provider) | ||||
|     { | ||||
|         //[UMetaData("key","value")] | ||||
|         List<CustomAttribute> metaDataAttributes = provider.CustomAttributes.FindMetaDataAttributes(); | ||||
|         foreach (var attrib in metaDataAttributes) | ||||
|         { | ||||
|             switch (attrib.ConstructorArguments.Count) | ||||
|             { | ||||
|                 case < 1: | ||||
|                     continue; | ||||
|                 case 1:  | ||||
|                     TryAddMetaData((string)attrib.ConstructorArguments[0].Value); | ||||
|                     break; | ||||
|                 default:  | ||||
|                     TryAddMetaData((string)attrib.ConstructorArguments[0].Value, (string)attrib.ConstructorArguments[1].Value); | ||||
|                     break; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private void AddMetaTagsNamespace()  | ||||
|     { | ||||
|         AddMetaTagsNamespace(MemberDefinition); | ||||
|     } | ||||
|      | ||||
|     protected void AddMetaTagsNamespace(ICustomAttributeProvider provider)  | ||||
|     { | ||||
|         //Specific MetaData Tags - all attributes in the UnrealSharp.Attributes.MetaTags Namespace | ||||
|         List<CustomAttribute> metaDataAttributes = provider.CustomAttributes.FindMetaDataAttributesByNamespace(); | ||||
|         foreach (var attrib in metaDataAttributes) | ||||
|         { | ||||
|             var key = attrib.AttributeType.Name.Replace("Attribute", ""); | ||||
|             if (attrib.HasConstructorArguments) | ||||
|             { | ||||
|                 TryAddMetaData(key, attrib.ConstructorArguments[0].Value); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 TryAddMetaData(key, "true"); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private void AddBaseAttributes() | ||||
|     { | ||||
|         if (BaseAttribute == null) | ||||
|         { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         CustomAttributeArgument? displayNameArgument = BaseAttribute.FindAttributeField("DisplayName"); | ||||
|         if (displayNameArgument.HasValue) | ||||
|         { | ||||
|             TryAddMetaData("DisplayName", (string) displayNameArgument.Value.Value); | ||||
|         } | ||||
|  | ||||
|         CustomAttributeArgument? categoryArgument = BaseAttribute.FindAttributeField("Category"); | ||||
|         if (categoryArgument.HasValue) | ||||
|         { | ||||
|             TryAddMetaData("Category", (string) categoryArgument.Value.Value); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     protected bool GetBoolMetadata(string key) | ||||
|     { | ||||
|         if (!MetaData.TryGetValue(key, out var val)) | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
|          | ||||
|         return 0 == StringComparer.OrdinalIgnoreCase.Compare(val, "true"); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,200 @@ | ||||
| using Mono.Cecil; | ||||
| using Mono.Cecil.Rocks; | ||||
| using UnrealSharpWeaver.TypeProcessors; | ||||
| using UnrealSharpWeaver.Utilities; | ||||
|  | ||||
| namespace UnrealSharpWeaver.MetaData; | ||||
|  | ||||
| public class ClassMetaData : TypeReferenceMetadata | ||||
| { | ||||
|     public TypeReferenceMetadata ParentClass { get; set; } | ||||
|     public List<PropertyMetaData> Properties { get; set; } | ||||
|     public List<FunctionMetaData> Functions { get; set; } | ||||
|     public List<FunctionMetaData> VirtualFunctions { get; set; } | ||||
|     public List<TypeReferenceMetadata> Interfaces { get; set; } | ||||
|     public string ConfigCategory { get; set; }  | ||||
|     public ClassFlags ClassFlags { get; set; } | ||||
|      | ||||
|     // Non-serialized for JSON | ||||
|     public bool HasProperties => Properties.Count > 0; | ||||
|     private readonly TypeDefinition _classDefinition; | ||||
|     // End non-serialized | ||||
|      | ||||
|     public ClassMetaData(TypeDefinition type) : base(type, TypeDefinitionUtilities.UClassAttribute) | ||||
|     { | ||||
|         _classDefinition = type; | ||||
|          | ||||
|         Properties = []; | ||||
|         Functions = []; | ||||
|         VirtualFunctions = []; | ||||
|          | ||||
|         ConfigCategory = string.Empty; | ||||
|         Interfaces = []; | ||||
|          | ||||
|         PopulateInterfaces(); | ||||
|         PopulateProperties(); | ||||
|         PopulateFunctions(); | ||||
|          | ||||
|         AddConfigCategory(); | ||||
|          | ||||
|         ParentClass = new TypeReferenceMetadata(type.BaseType.Resolve()); | ||||
|         ClassFlags |= GetClassFlags(type, AttributeName) | ClassFlags.CompiledFromBlueprint; | ||||
|          | ||||
|         // Force DefaultConfig if Config is set and no other config flag is set | ||||
|         if (ClassFlags.HasFlag(ClassFlags.Config) && | ||||
|             !ClassFlags.HasFlag(ClassFlags.GlobalUserConfig | ClassFlags.DefaultConfig | ClassFlags.ProjectUserConfig)) | ||||
|         { | ||||
|             ClassFlags |= ClassFlags.DefaultConfig; | ||||
|         } | ||||
|  | ||||
|         if (type.IsChildOf(WeaverImporter.Instance.UActorComponentDefinition)) | ||||
|         { | ||||
|             TryAddMetaData("BlueprintSpawnableComponent", true); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private void AddConfigCategory() | ||||
|     { | ||||
|         CustomAttribute uClassAttribute = _classDefinition.GetUClass()!; | ||||
|         CustomAttributeArgument? configCategoryProperty = uClassAttribute.FindAttributeField(nameof(ConfigCategory)); | ||||
|         if (configCategoryProperty != null) | ||||
|         { | ||||
|             ConfigCategory = (string) configCategoryProperty.Value.Value; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private void PopulateProperties() | ||||
|     { | ||||
|         if (_classDefinition.Properties.Count == 0) | ||||
|         { | ||||
|             return; | ||||
|         } | ||||
|          | ||||
|         Properties = []; | ||||
|          | ||||
|         foreach (PropertyDefinition property in _classDefinition.Properties) | ||||
|         { | ||||
|             CustomAttribute? uPropertyAttribute = property.GetUProperty(); | ||||
|  | ||||
|             if (uPropertyAttribute == null) | ||||
|             { | ||||
|                 continue; | ||||
|             } | ||||
|              | ||||
|             PropertyMetaData propertyMetaData = new PropertyMetaData(property); | ||||
|             Properties.Add(propertyMetaData); | ||||
|                  | ||||
|             if (propertyMetaData.IsInstancedReference) | ||||
|             { | ||||
|                 ClassFlags |= ClassFlags.HasInstancedReference; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     void PopulateFunctions() | ||||
|     { | ||||
|         if (_classDefinition.Methods.Count == 0) | ||||
|         { | ||||
|             return; | ||||
|         } | ||||
|          | ||||
|         Functions = []; | ||||
|         VirtualFunctions = []; | ||||
|          | ||||
|         for (var i = _classDefinition.Methods.Count - 1; i >= 0; i--) | ||||
|         { | ||||
|             MethodDefinition method = _classDefinition.Methods[i]; | ||||
|  | ||||
|             if (method.HasParameters) | ||||
|             { | ||||
|                 var paramNameSet = new HashSet<string>(); | ||||
|                 var uniqueNum = 0; | ||||
|                 foreach (var param in method.Parameters) | ||||
|                 { | ||||
|                     if (!paramNameSet.Add(param.Name)) | ||||
|                     { | ||||
|                         param.Name = $"{param.Name}_{uniqueNum++}"; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             if (FunctionMetaData.IsAsyncUFunction(method)) | ||||
|             { | ||||
|                 method.CustomAttributes.Clear(); | ||||
|                 continue; | ||||
|             } | ||||
|              | ||||
|             bool isBlueprintOverride = FunctionMetaData.IsBlueprintEventOverride(method); | ||||
|             bool isInterfaceFunction = FunctionMetaData.IsInterfaceFunction(method); | ||||
|              | ||||
|             if (method.IsUFunction() && !isInterfaceFunction) | ||||
|             { | ||||
|                 if (isBlueprintOverride) | ||||
|                 { | ||||
|                     throw new Exception($"{method.FullName} is a Blueprint override and cannot be marked as a UFunction again."); | ||||
|                 } | ||||
|                  | ||||
|                 FunctionMetaData functionMetaData = new FunctionMetaData(method); | ||||
|                  | ||||
|                 if (isInterfaceFunction && functionMetaData.FunctionFlags.HasFlag(EFunctionFlags.BlueprintNativeEvent)) | ||||
|                 { | ||||
|                     throw new Exception("Interface functions cannot be marked as BlueprintEvent. Mark base declaration as BlueprintEvent instead."); | ||||
|                 } | ||||
|                  | ||||
|                 Functions.Add(functionMetaData); | ||||
|             } | ||||
|              | ||||
|             if (isBlueprintOverride || isInterfaceFunction && method.GetBaseMethod().DeclaringType == _classDefinition) | ||||
|             { | ||||
|                 EFunctionFlags functionFlags = EFunctionFlags.None; | ||||
|                 if (isInterfaceFunction) | ||||
|                 { | ||||
|                     MethodDefinition interfaceFunction = FunctionMetaData.TryGetInterfaceFunction(method)!; | ||||
|                     functionFlags = interfaceFunction.GetFunctionFlags(); | ||||
|                 } | ||||
|                  | ||||
|                 VirtualFunctions.Add(new FunctionMetaData(method, false, functionFlags)); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private static ClassFlags GetClassFlags(TypeReference classReference, string flagsAttributeName) | ||||
|     { | ||||
|         return (ClassFlags) GetFlags(classReference.Resolve().CustomAttributes, flagsAttributeName); | ||||
|     } | ||||
|      | ||||
|     void PopulateInterfaces() | ||||
|     { | ||||
|         if (_classDefinition.Interfaces.Count == 0) | ||||
|         { | ||||
|             return; | ||||
|         } | ||||
|          | ||||
|         Interfaces = []; | ||||
|          | ||||
|         foreach (InterfaceImplementation? typeInterface in _classDefinition.Interfaces) | ||||
|         { | ||||
|             TypeDefinition interfaceType = typeInterface.InterfaceType.Resolve(); | ||||
|  | ||||
|             if (interfaceType == WeaverImporter.Instance.IInterfaceType || !interfaceType.IsUInterface()) | ||||
|             { | ||||
|                 continue; | ||||
|             } | ||||
|              | ||||
|             Interfaces.Add(new TypeReferenceMetadata(interfaceType)); | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     public void PostWeaveCleanup() | ||||
|     { | ||||
|         foreach (FunctionMetaData function in Functions) | ||||
|         { | ||||
|             function.TryRemoveMethod(); | ||||
|         } | ||||
|          | ||||
|         foreach (FunctionMetaData virtualFunction in VirtualFunctions) | ||||
|         { | ||||
|             virtualFunction.TryRemoveMethod(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,17 @@ | ||||
| using Mono.Cecil; | ||||
| using UnrealSharpWeaver.Utilities; | ||||
|  | ||||
| namespace UnrealSharpWeaver.MetaData; | ||||
|  | ||||
| public class DelegateMetaData : TypeReferenceMetadata | ||||
| { | ||||
|     public FunctionMetaData Signature { get; set; } | ||||
|      | ||||
|     public DelegateMetaData(FunctionMetaData signature, TypeReference member, string attributeName = "", EFunctionFlags functionFlags = EFunctionFlags.None) : base(member, attributeName) | ||||
|     { | ||||
|         Name = DelegateUtilities.GetUnrealDelegateName(member); | ||||
|          | ||||
|         Signature = signature; | ||||
|         Signature.FunctionFlags |= functionFlags; | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,24 @@ | ||||
| using Mono.Cecil; | ||||
| using UnrealSharpWeaver.Utilities; | ||||
|  | ||||
| namespace UnrealSharpWeaver.MetaData; | ||||
|  | ||||
| public class EnumMetaData : TypeReferenceMetadata | ||||
| { | ||||
|     public List<string> Items { get; set; } | ||||
|  | ||||
|     public EnumMetaData(TypeDefinition enumType) : base(enumType, TypeDefinitionUtilities.UEnumAttribute) | ||||
|     { | ||||
|         Items = new List<string>(); | ||||
|          | ||||
|         foreach (var field in enumType.Fields) | ||||
|         { | ||||
|             if (!field.IsStatic && field.Name == "value__") | ||||
|             { | ||||
|                 continue; | ||||
|             } | ||||
|              | ||||
|             Items.Add(field.Name); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,293 @@ | ||||
| using Mono.Cecil; | ||||
| using Mono.Cecil.Cil; | ||||
| using Mono.Cecil.Rocks; | ||||
| using UnrealSharpWeaver.TypeProcessors; | ||||
| using UnrealSharpWeaver.Utilities; | ||||
|  | ||||
| namespace UnrealSharpWeaver.MetaData; | ||||
|  | ||||
| public class FunctionMetaData : BaseMetaData | ||||
| {  | ||||
|     public PropertyMetaData[] Parameters { get; set; } | ||||
|     public PropertyMetaData? ReturnValue { get; set; } | ||||
|     public EFunctionFlags FunctionFlags { get; set; } | ||||
|      | ||||
|     // Non-serialized for JSON | ||||
|     public readonly MethodDefinition MethodDef; | ||||
|     public FunctionRewriteInfo RewriteInfo; | ||||
|     public FieldDefinition? FunctionPointerField; | ||||
|     public bool IsBlueprintEvent => FunctionFlags.HasAnyFlags(EFunctionFlags.BlueprintNativeEvent); | ||||
|     public bool HasParameters => Parameters.Length > 0 || HasReturnValue; | ||||
|     public bool HasReturnValue => ReturnValue != null; | ||||
|     public bool IsRpc => FunctionFlags.HasAnyFlags(Utilities.MethodUtilities.RpcFlags); | ||||
|     public bool HasOutParams => FunctionFlags.HasAnyFlags(EFunctionFlags.HasOutParms); | ||||
|     private bool _shouldBeRemoved; | ||||
|     // End non-serialized | ||||
|  | ||||
|     private const string CallInEditorName = "CallInEditor"; | ||||
|  | ||||
|     public FunctionMetaData(MethodDefinition method, bool onlyCollectMetaData = false, EFunctionFlags functionFlags = EFunctionFlags.None)  | ||||
|         : base(method, Utilities.MethodUtilities.UFunctionAttribute) | ||||
|     { | ||||
|         MethodDef = method; | ||||
|         FunctionFlags = functionFlags; | ||||
|          | ||||
|         bool hasOutParams = false; | ||||
|          | ||||
|         if (!method.ReturnsVoid()) | ||||
|         { | ||||
|             hasOutParams = true; | ||||
|             try | ||||
|             { | ||||
|                 ReturnValue = PropertyMetaData.FromTypeReference(method.ReturnType, "ReturnValue", ParameterType.ReturnValue); | ||||
|             } | ||||
|             catch (InvalidPropertyException) | ||||
|             { | ||||
|                 throw new InvalidUnrealFunctionException(method, $"'{method.ReturnType.FullName}' is invalid for unreal function return value."); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (BaseAttribute != null) | ||||
|         { | ||||
|             CustomAttributeArgument? callInEditor = BaseAttribute.FindAttributeField(CallInEditorName); | ||||
|             if (callInEditor.HasValue) | ||||
|             { | ||||
|                 TryAddMetaData(CallInEditorName, (bool) callInEditor.Value.Value); | ||||
|             } | ||||
|         } | ||||
|          | ||||
|         Parameters = new PropertyMetaData[method.Parameters.Count]; | ||||
|         for (int i = 0; i < method.Parameters.Count; ++i) | ||||
|         { | ||||
|             ParameterDefinition param = method.Parameters[i]; | ||||
|             ParameterType modifier = ParameterType.Value; | ||||
|             TypeReference paramType = param.ParameterType; | ||||
|              | ||||
|             if (param.IsOut) | ||||
|             { | ||||
|                 hasOutParams = true; | ||||
|                 modifier = ParameterType.Out; | ||||
|             } | ||||
|             else if (paramType.IsByReference) | ||||
|             { | ||||
|                 hasOutParams = true; | ||||
|                 modifier = ParameterType.Ref; | ||||
|             } | ||||
|  | ||||
|             Parameters[i] = PropertyMetaData.FromTypeReference(paramType, param.Name, modifier, param); | ||||
|  | ||||
|             if (param.HasConstant) | ||||
|             { | ||||
|                 string? defaultValue = DefaultValueToString(param); | ||||
|                 if (defaultValue != null) | ||||
|                 { | ||||
|                     TryAddMetaData($"CPP_Default_{param.Name}", defaultValue); | ||||
|                     FunctionFlags |= EFunctionFlags.HasDefaults; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|          | ||||
|         FunctionFlags |= MethodDef.GetFunctionFlags(); | ||||
|          | ||||
|         if (hasOutParams) | ||||
|         { | ||||
|             FunctionFlags |= EFunctionFlags.HasOutParms; | ||||
|         } | ||||
|          | ||||
|         if (onlyCollectMetaData) | ||||
|         { | ||||
|             return; | ||||
|         } | ||||
|          | ||||
|         RewriteFunction(); | ||||
|     } | ||||
|      | ||||
|     public void RewriteFunction(bool forceOverwriteBody = false) | ||||
|     { | ||||
|         TypeDefinition baseType = MethodDef.GetOriginalBaseMethod().DeclaringType; | ||||
|         if (baseType == MethodDef.DeclaringType) | ||||
|         { | ||||
|             RewriteInfo = new FunctionRewriteInfo(this); | ||||
|             FunctionProcessor.PrepareFunctionForRewrite(this, MethodDef.DeclaringType, forceOverwriteBody); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             EFunctionFlags flags = GetFunctionFlags(MethodDef.GetOriginalBaseMethod()); | ||||
|             if (flags.HasAnyFlags(EFunctionFlags.BlueprintCallable)  | ||||
|                 && !flags.HasAnyFlags(EFunctionFlags.BlueprintNativeEvent)) | ||||
|             { | ||||
|                 return; | ||||
|             } | ||||
|              | ||||
|             FunctionProcessor.MakeImplementationMethod(this); | ||||
|              | ||||
|             // We don't need the override anymore. It's copied into the Implementation method. | ||||
|             // But we can't remove it here because it would mess up for child classes during weaving. | ||||
|             _shouldBeRemoved = true; | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     public void TryRemoveMethod() | ||||
|     { | ||||
|         if (!_shouldBeRemoved) | ||||
|         { | ||||
|             return; | ||||
|         } | ||||
|          | ||||
|         MethodDef.DeclaringType.Methods.Remove(MethodDef); | ||||
|     } | ||||
|      | ||||
|     public static bool IsAsyncUFunction(MethodDefinition method) | ||||
|     { | ||||
|         if (!method.HasCustomAttributes) | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         CustomAttribute? functionAttribute = method.GetUFunction(); | ||||
|         if (functionAttribute == null) | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         if (!functionAttribute.HasConstructorArguments) | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         var flags = (EFunctionFlags) (ulong) functionAttribute.ConstructorArguments[0].Value; | ||||
|         return flags == EFunctionFlags.BlueprintCallable && method.ReturnType.FullName.StartsWith("System.Threading.Tasks.Task"); | ||||
|     } | ||||
|  | ||||
|     public static bool IsBlueprintEventOverride(MethodDefinition method) | ||||
|     { | ||||
|         if (!method.IsVirtual) | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
|          | ||||
|         MethodDefinition baseMethod = method.GetOriginalBaseMethod(); | ||||
|         if (baseMethod != method && baseMethod.HasCustomAttributes) | ||||
|         { | ||||
|             return baseMethod.IsUFunction(); | ||||
|         } | ||||
|  | ||||
|         return false; | ||||
|     } | ||||
|      | ||||
|     public static string? DefaultValueToString(ParameterDefinition value) | ||||
|     { | ||||
|         // Can be null if the value is set to = default/null | ||||
|         if (value.Constant == null) | ||||
|         { | ||||
|             return null; | ||||
|         } | ||||
|          | ||||
|         TypeDefinition typeDefinition = value.ParameterType.Resolve(); | ||||
|         if (typeDefinition.IsEnum) | ||||
|         { | ||||
|             return typeDefinition.Fields[(byte) value.Constant].Name; | ||||
|         } | ||||
|          | ||||
|         // Unreal doesn't support commas in default values | ||||
|         string defaultValue = value.Constant.ToString()!; | ||||
|         defaultValue = defaultValue.Replace(",", "."); | ||||
|          | ||||
|         return defaultValue; | ||||
|     } | ||||
|  | ||||
|     public static EFunctionFlags GetFunctionFlags(MethodDefinition method) | ||||
|     { | ||||
|         return (EFunctionFlags) GetFlags(method, "FunctionFlagsMapAttribute"); | ||||
|     } | ||||
|  | ||||
|     public static bool IsInterfaceFunction(MethodDefinition method) | ||||
|     { | ||||
|         return TryGetInterfaceFunction(method) != null; | ||||
|     } | ||||
|      | ||||
|     public static MethodDefinition? TryGetInterfaceFunction(MethodDefinition method) | ||||
|     { | ||||
|         foreach (var typeInterface in method.DeclaringType.Interfaces) | ||||
|         { | ||||
|             var interfaceType = typeInterface.InterfaceType.Resolve(); | ||||
|              | ||||
|             if (!interfaceType.IsUInterface()) | ||||
|             { | ||||
|                 continue;  | ||||
|             } | ||||
|  | ||||
|             foreach (MethodDefinition? interfaceMethod in interfaceType.Methods) | ||||
|             { | ||||
|                 if (interfaceMethod.Name == method.Name) | ||||
|                 { | ||||
|                     return interfaceMethod; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|          | ||||
|         return null; | ||||
|     } | ||||
|      | ||||
|     public static bool IsBlueprintCallable(MethodDefinition method) | ||||
|     { | ||||
|         EFunctionFlags flags = GetFunctionFlags(method); | ||||
|         return flags.HasAnyFlags(EFunctionFlags.BlueprintCallable); | ||||
|     } | ||||
|      | ||||
|     public void EmitFunctionPointers(ILProcessor processor, Instruction loadTypeField, Instruction setFunctionPointer) | ||||
|     { | ||||
|         processor.Append(loadTypeField); | ||||
|         processor.Emit(OpCodes.Ldstr, Name); | ||||
|         processor.Emit(OpCodes.Call, WeaverImporter.Instance.GetNativeFunctionFromClassAndNameMethod); | ||||
|         processor.Append(setFunctionPointer); | ||||
|     } | ||||
|      | ||||
|     public void EmitFunctionParamOffsets(ILProcessor processor, Instruction loadFunctionPointer) | ||||
|     { | ||||
|         foreach (FunctionParamRewriteInfo paramRewriteInfo in RewriteInfo.FunctionParams) | ||||
|         { | ||||
|             FieldDefinition? offsetField = paramRewriteInfo.OffsetField; | ||||
|             if (offsetField == null) | ||||
|             { | ||||
|                 continue; | ||||
|             } | ||||
|  | ||||
|             PropertyMetaData param = paramRewriteInfo.PropertyMetaData; | ||||
|                  | ||||
|             processor.Append(loadFunctionPointer); | ||||
|             processor.Emit(OpCodes.Ldstr, param.Name); | ||||
|             processor.Emit(OpCodes.Call, WeaverImporter.Instance.GetPropertyOffsetFromNameMethod); | ||||
|             processor.Emit(OpCodes.Stsfld, offsetField); | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     public void EmitFunctionParamSize(ILProcessor processor, Instruction loadFunctionPointer) | ||||
|     { | ||||
|         if (RewriteInfo.FunctionParamSizeField == null) | ||||
|         { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         processor.Append(loadFunctionPointer); | ||||
|         processor.Emit(OpCodes.Call, WeaverImporter.Instance.GetNativeFunctionParamsSizeMethod); | ||||
|         processor.Emit(OpCodes.Stsfld, RewriteInfo.FunctionParamSizeField); | ||||
|     } | ||||
|      | ||||
|     public void EmitParamNativeProperty(ILProcessor processor, Instruction? loadFunctionPointer) | ||||
|     { | ||||
|         foreach (var paramRewriteInfo in RewriteInfo.FunctionParams) | ||||
|         { | ||||
|             FieldDefinition? nativePropertyField = paramRewriteInfo.NativePropertyField; | ||||
|             if (nativePropertyField == null) | ||||
|             { | ||||
|                 continue; | ||||
|             } | ||||
|  | ||||
|             processor.Append(loadFunctionPointer); | ||||
|             processor.Emit(OpCodes.Ldstr, paramRewriteInfo.PropertyMetaData.Name); | ||||
|             processor.Emit(OpCodes.Call, WeaverImporter.Instance.GetNativePropertyFromNameMethod); | ||||
|             processor.Emit(OpCodes.Stsfld, nativePropertyField); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,49 @@ | ||||
| using Mono.Cecil; | ||||
| using UnrealSharpWeaver.Utilities; | ||||
|  | ||||
| namespace UnrealSharpWeaver.MetaData; | ||||
|  | ||||
| public class InterfaceMetaData : TypeReferenceMetadata | ||||
| {  | ||||
|     public TypeReferenceMetadata ParentInterface { get; set; } | ||||
|     public List<FunctionMetaData> Functions { get; set; } | ||||
|      | ||||
|     // Non-serialized for JSON | ||||
|     const string CannotImplementInterfaceInBlueprint = "CannotImplementInterfaceInBlueprint"; | ||||
|     // End non-serialized | ||||
|      | ||||
|     public InterfaceMetaData(TypeDefinition typeDefinition) : base(typeDefinition, TypeDefinitionUtilities.UInterfaceAttribute) | ||||
|     { | ||||
|         Functions = []; | ||||
|          | ||||
|         if (typeDefinition.HasInterfaces) | ||||
|         { | ||||
|             foreach (InterfaceImplementation? interfaceType in typeDefinition.Interfaces) | ||||
|             { | ||||
|                 TypeDefinition interfaceDef = interfaceType.InterfaceType.Resolve(); | ||||
|                 if (!interfaceDef.IsUInterface()) | ||||
|                 { | ||||
|                     continue; | ||||
|                 } | ||||
|                  | ||||
|                 ParentInterface = new TypeReferenceMetadata(interfaceDef); | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|          | ||||
|         foreach (var method in typeDefinition.Methods) | ||||
|         { | ||||
|             if (method.IsAbstract && method.IsUFunction()) | ||||
|             { | ||||
|                 Functions.Add(new FunctionMetaData(method, onlyCollectMetaData: true)); | ||||
|             } | ||||
|         } | ||||
|          | ||||
|         CustomAttributeArgument? nonBpInterface = BaseAttribute!.FindAttributeField(CannotImplementInterfaceInBlueprint); | ||||
|         if (nonBpInterface != null) | ||||
|         { | ||||
|             TryAddMetaData(CannotImplementInterfaceInBlueprint, (bool) nonBpInterface.Value.Value); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,291 @@ | ||||
| using System.Text.Json.Serialization; | ||||
| using Mono.Cecil; | ||||
| using Mono.Cecil.Cil; | ||||
| using UnrealSharpWeaver.NativeTypes; | ||||
| using UnrealSharpWeaver.Utilities; | ||||
|  | ||||
| namespace UnrealSharpWeaver.MetaData; | ||||
|  | ||||
| public class PropertyMetaData : BaseMetaData | ||||
| { | ||||
|     public PropertyFlags PropertyFlags { get; set; } = PropertyFlags.None; | ||||
|     public NativeDataType PropertyDataType { get; set; } = null!; | ||||
|     public string RepNotifyFunctionName { get; set; } = string.Empty; | ||||
|     public LifetimeCondition LifetimeCondition { get; set; } = LifetimeCondition.None; | ||||
|     public string BlueprintSetter { get; set; } = string.Empty; | ||||
|     public string BlueprintGetter { get; set; } = string.Empty; | ||||
|     public bool HasCustomAccessors { get; set; } = false; | ||||
|     [JsonIgnore] | ||||
|     public PropertyDefinition? GeneratedAccessorProperty { get; set; } = null; | ||||
|  | ||||
|     // Non-serialized for JSON | ||||
|     public FieldDefinition? PropertyOffsetField; | ||||
|     public FieldDefinition? NativePropertyField; | ||||
|     public readonly MemberReference? MemberRef; | ||||
|     public bool IsOutParameter => (PropertyFlags & PropertyFlags.OutParm) == PropertyFlags.OutParm; | ||||
|     public bool IsReferenceParameter => (PropertyFlags & PropertyFlags.ReferenceParm) == PropertyFlags.ReferenceParm; | ||||
|     public bool IsReturnParameter => (PropertyFlags & PropertyFlags.ReturnParm) == PropertyFlags.ReturnParm; | ||||
|     public bool IsInstancedReference => (PropertyFlags & PropertyFlags.InstancedReference) == PropertyFlags.InstancedReference; | ||||
|     // End non-serialized | ||||
|      | ||||
|     private PropertyMetaData(MemberReference memberRef) : base(memberRef, PropertyUtilities.UPropertyAttribute) | ||||
|     { | ||||
|  | ||||
|     } | ||||
|  | ||||
|     private PropertyMetaData(TypeReference typeRef, string paramName, ParameterType modifier) : this(typeRef) | ||||
|     { | ||||
|         MemberRef = typeRef; | ||||
|         Name = paramName; | ||||
|         PropertyDataType = typeRef.GetDataType(paramName, null); | ||||
|          | ||||
|         PropertyFlags flags = PropertyFlags.None; | ||||
|          | ||||
|         if (modifier != ParameterType.None) | ||||
|         { | ||||
|             flags |= PropertyFlags.Parm; | ||||
|         } | ||||
|          | ||||
|         switch (modifier) | ||||
|         { | ||||
|             case ParameterType.Out: | ||||
|                 flags |= PropertyFlags.OutParm; | ||||
|                 break; | ||||
|             case ParameterType.Ref: | ||||
|                 flags |= PropertyFlags.OutParm | PropertyFlags.ReferenceParm; | ||||
|                 break; | ||||
|             case ParameterType.ReturnValue: | ||||
|                 flags |= PropertyFlags.ReturnParm | PropertyFlags.OutParm; | ||||
|                 break; | ||||
|         } | ||||
|  | ||||
|         PropertyFlags = flags; | ||||
|     } | ||||
|  | ||||
|     public PropertyMetaData(PropertyDefinition property) : this((MemberReference) property) | ||||
|     { | ||||
|         MemberRef = property; | ||||
|          | ||||
|         MethodDefinition getter = property.GetMethod; | ||||
|         MethodDefinition setter = property.SetMethod; | ||||
|  | ||||
|         if (getter == null) | ||||
|         { | ||||
|             throw new InvalidPropertyException(property, "Unreal properties must have a default get method"); | ||||
|         } | ||||
|          | ||||
|         // Check if we have custom accessors | ||||
|         bool hasCustomGetter = !getter.MethodIsCompilerGenerated(); | ||||
|         bool hasCustomSetter = setter != null && !setter.MethodIsCompilerGenerated(); | ||||
|  | ||||
|         HasCustomAccessors = hasCustomGetter || hasCustomSetter; | ||||
|  | ||||
|         // Allow custom getter/setter implementations | ||||
|         if (!HasCustomAccessors) | ||||
|         { | ||||
|             // Only throw exception if not a custom accessor | ||||
|             if (!getter.MethodIsCompilerGenerated()) | ||||
|             { | ||||
|                 throw new InvalidPropertyException(property, "Getter can not have a body for Unreal properties unless it's a custom accessor"); | ||||
|             } | ||||
|  | ||||
|             if (setter != null && !setter.MethodIsCompilerGenerated()) | ||||
|             { | ||||
|                 throw new InvalidPropertyException(property, "Setter can not have a body for Unreal properties unless it's a custom accessor"); | ||||
|             } | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             // Register custom accessors as UFunctions if they have BlueprintGetter/Setter specified | ||||
|             RegisterPropertyAccessorAsUFunction(property.GetMethod, true); | ||||
|             RegisterPropertyAccessorAsUFunction(property.SetMethod, false); | ||||
|         } | ||||
|          | ||||
|         if (getter.IsPrivate && PropertyFlags.HasFlag(PropertyFlags.BlueprintVisible)) | ||||
|         { | ||||
|             if(!GetBoolMetadata("AllowPrivateAccess")) | ||||
|             { | ||||
|                 throw new InvalidPropertyException(property, "Blueprint visible properties can not be set to private."); | ||||
|             }  | ||||
|         } | ||||
|          | ||||
|         Initialize(property, property.PropertyType); | ||||
|     } | ||||
|  | ||||
|     public PropertyMetaData(FieldDefinition property) : this((MemberReference) property) | ||||
|     { | ||||
|         MemberRef = property; | ||||
|         Initialize(property, property.FieldType); | ||||
|     } | ||||
|      | ||||
|     private void Initialize(IMemberDefinition property, TypeReference propertyType) | ||||
|     { | ||||
|         Name = property.Name; | ||||
|         PropertyDataType = propertyType.GetDataType(property.FullName, property.CustomAttributes); | ||||
|         PropertyFlags flags = (PropertyFlags) GetFlags(property, "PropertyFlagsMapAttribute"); | ||||
|          | ||||
|         CustomAttribute? upropertyAttribute = property.GetUProperty(); | ||||
|         if (upropertyAttribute == null) | ||||
|         { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         CustomAttributeArgument? blueprintSetterArgument = upropertyAttribute.FindAttributeField("BlueprintSetter"); | ||||
|         if (blueprintSetterArgument.HasValue) | ||||
|         { | ||||
|             BlueprintSetter = (string) blueprintSetterArgument.Value.Value; | ||||
|         } | ||||
|          | ||||
|         CustomAttributeArgument? blueprintGetterArgument = upropertyAttribute.FindAttributeField("BlueprintGetter"); | ||||
|         if (blueprintGetterArgument.HasValue) | ||||
|         { | ||||
|             BlueprintGetter = (string) blueprintGetterArgument.Value.Value; | ||||
|         } | ||||
|          | ||||
|         CustomAttributeArgument? lifetimeConditionField = upropertyAttribute.FindAttributeField("LifetimeCondition"); | ||||
|         if (lifetimeConditionField.HasValue) | ||||
|         { | ||||
|             LifetimeCondition = (LifetimeCondition) lifetimeConditionField.Value.Value; | ||||
|         } | ||||
|          | ||||
|         CustomAttributeArgument? notifyMethodArgument = upropertyAttribute.FindAttributeField("ReplicatedUsing"); | ||||
|         if (notifyMethodArgument.HasValue) | ||||
|         { | ||||
|             string notifyMethodName = (string) notifyMethodArgument.Value.Value; | ||||
|             MethodReference? notifyMethod = property.DeclaringType.FindMethod(notifyMethodName); | ||||
|              | ||||
|             if (notifyMethod == null) | ||||
|             { | ||||
|                 throw new InvalidPropertyException(property, $"RepNotify method '{notifyMethodName}' not found on {property.DeclaringType.Name}"); | ||||
|             } | ||||
|              | ||||
|             if (!notifyMethod.Resolve().IsUFunction()) | ||||
|             { | ||||
|                 throw new InvalidPropertyException(property, $"RepNotify method '{notifyMethodName}' needs to be declared as a UFunction."); | ||||
|             } | ||||
|  | ||||
|             if (!notifyMethod.ReturnsVoid()) | ||||
|             { | ||||
|                 throw new InvalidPropertyException(property, $"RepNotify method '{notifyMethodName}' must return void"); | ||||
|             } | ||||
|  | ||||
|             if (notifyMethod.Parameters.Count > 0) | ||||
|             { | ||||
|                 if (notifyMethod.Parameters[0].ParameterType != propertyType) | ||||
|                 { | ||||
|                     throw new InvalidPropertyException(property, $"RepNotify can only have matching parameters to the property it is notifying. '{notifyMethodName}' takes a '{notifyMethod.Parameters[0].ParameterType.FullName}' but the property is a '{propertyType.FullName}'"); | ||||
|                 } | ||||
|                  | ||||
|                 if (notifyMethod.Parameters.Count > 1) | ||||
|                 { | ||||
|                     throw new InvalidPropertyException(property, $"RepNotify method '{notifyMethodName}' must take a single argument"); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             // Just a quality of life, if the property is set to ReplicatedUsing, it should be replicating | ||||
|             flags |= PropertyFlags.Net | PropertyFlags.RepNotify; | ||||
|             RepNotifyFunctionName = notifyMethodName; | ||||
|         } | ||||
|          | ||||
|         if (flags.HasFlag(PropertyFlags.Net) && !PropertyDataType.IsNetworkSupported) | ||||
|         { | ||||
|             throw new InvalidPropertyException(property, $"{Name} is marked as replicated but the {PropertyDataType.CSharpType} is not supported for replication"); | ||||
|         } | ||||
|          | ||||
|         bool isDefaultComponent = NativeDataDefaultComponent.IsDefaultComponent(property.CustomAttributes); | ||||
|         bool isPersistentInstance = (flags & PropertyFlags.PersistentInstance) != 0; | ||||
|  | ||||
|         const PropertyFlags instancedFlags = PropertyFlags.InstancedReference | PropertyFlags.ExportObject; | ||||
|          | ||||
|         if ((flags & PropertyFlags.InstancedReference) != 0 || isPersistentInstance) | ||||
|         { | ||||
|             flags |= instancedFlags; | ||||
|         } | ||||
|          | ||||
|         if (isDefaultComponent) | ||||
|         { | ||||
|             flags = PropertyFlags.BlueprintVisible | PropertyFlags.NonTransactional | PropertyFlags.InstancedReference; | ||||
|         } | ||||
|  | ||||
|         if (isPersistentInstance || isDefaultComponent) | ||||
|         { | ||||
|             TryAddMetaData("EditInline", "true"); | ||||
|         } | ||||
|          | ||||
|         PropertyFlags = flags; | ||||
|     } | ||||
|      | ||||
|     public void InitializePropertyPointers(ILProcessor processor, Instruction loadNativeType, Instruction setPropertyPointer) | ||||
|     { | ||||
|         processor.Append(loadNativeType); | ||||
|         processor.Emit(OpCodes.Ldstr, Name); | ||||
|         processor.Emit(OpCodes.Call, WeaverImporter.Instance.GetNativePropertyFromNameMethod); | ||||
|         processor.Append(setPropertyPointer); | ||||
|     } | ||||
|      | ||||
|     public void InitializePropertyOffsets(ILProcessor processor, Instruction loadNativeType) | ||||
|     { | ||||
|         processor.Append(loadNativeType); | ||||
|         processor.Emit(OpCodes.Call, WeaverImporter.Instance.GetPropertyOffset); | ||||
|         processor.Emit(OpCodes.Stsfld, PropertyOffsetField); | ||||
|     } | ||||
|      | ||||
|     public static PropertyMetaData FromTypeReference(TypeReference typeRef, string paramName, ParameterType modifier = ParameterType.None, ParameterDefinition? parameterDefinition = null) | ||||
|     { | ||||
|         var metadata = new PropertyMetaData(typeRef, paramName, modifier); | ||||
|         if (parameterDefinition is null) return metadata; | ||||
|         metadata.AddMetaData(parameterDefinition); | ||||
|         metadata.AddMetaTagsNamespace(parameterDefinition); | ||||
|         return metadata; | ||||
|     } | ||||
|      | ||||
|     private void RegisterPropertyAccessorAsUFunction(MethodDefinition accessorMethod, bool isGetter) | ||||
|     { | ||||
|         if (accessorMethod == null) | ||||
|         { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         // Set the appropriate blueprint accessor name based on getter or setter | ||||
|         if (isGetter) | ||||
|         { | ||||
|             BlueprintGetter = accessorMethod.Name; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             BlueprintSetter = accessorMethod.Name; | ||||
|         } | ||||
|  | ||||
|         // Add UFunction attribute if not already present | ||||
|         if (!accessorMethod.IsUFunction()) | ||||
|         { | ||||
|             var ufunctionCtor = WeaverImporter.Instance.CurrentWeavingAssembly.MainModule.ImportReference( | ||||
|                 WeaverImporter.Instance.UFunctionAttributeConstructor); | ||||
|  | ||||
|             // Create constructor arguments array | ||||
|             var ctorArgs = new[] | ||||
|             { | ||||
|                 // First argument - FunctionFlags (combine BlueprintCallable with BlueprintPure for getters) | ||||
|                 new CustomAttributeArgument( | ||||
|                     WeaverImporter.Instance.UInt64TypeRef, | ||||
|                     (ulong)(isGetter  | ||||
|                         ? EFunctionFlags.BlueprintCallable | EFunctionFlags.BlueprintPure  | ||||
|                         : EFunctionFlags.BlueprintCallable)) | ||||
|             }; | ||||
|  | ||||
|             var ufunctionAttribute = new CustomAttribute(ufunctionCtor) | ||||
|             { | ||||
|                 ConstructorArguments = { ctorArgs[0] } | ||||
|             }; | ||||
|  | ||||
|             accessorMethod.CustomAttributes.Add(ufunctionAttribute); | ||||
|              | ||||
|             var blueprintInternalUseOnlyCtor = WeaverImporter.Instance.CurrentWeavingAssembly.MainModule.ImportReference( | ||||
|                 WeaverImporter.Instance.BlueprintInternalUseAttributeConstructor); | ||||
|             accessorMethod.CustomAttributes.Add(new CustomAttribute(blueprintInternalUseOnlyCtor)); | ||||
|         } | ||||
|  | ||||
|         // Make the method public to be accessible from Blueprint | ||||
|         accessorMethod.Attributes = (accessorMethod.Attributes & ~MethodAttributes.MemberAccessMask) | MethodAttributes.Public; | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,92 @@ | ||||
| using System.Text.RegularExpressions; | ||||
| using Mono.Cecil; | ||||
| using UnrealSharpWeaver.Utilities; | ||||
| using PropertyUtilities = UnrealSharpWeaver.Utilities.PropertyUtilities; | ||||
|  | ||||
| namespace UnrealSharpWeaver.MetaData; | ||||
|  | ||||
| public partial class StructMetaData : TypeReferenceMetadata | ||||
| { | ||||
|     public List<PropertyMetaData> Fields { get; set; } | ||||
|     public StructFlags StructFlags { get; set; } | ||||
|      | ||||
|     // Non-serialized for JSON | ||||
|     public readonly bool IsBlittableStruct; | ||||
|     // End non-serialized | ||||
|      | ||||
|     public StructMetaData(TypeDefinition structDefinition) : base(structDefinition, TypeDefinitionUtilities.UStructAttribute) | ||||
|     { | ||||
|         Fields = new List<PropertyMetaData>(); | ||||
|         IsBlittableStruct = true; | ||||
|          | ||||
|  | ||||
|         var backingFieldRegex = BackingFieldRegex(); | ||||
|         foreach (var field in structDefinition.Fields) | ||||
|         { | ||||
|             if (field.IsStatic) | ||||
|             { | ||||
|                 continue; | ||||
|             } | ||||
|              | ||||
|             if (!field.IsUProperty()) | ||||
|             { | ||||
|                 // Struct is not blittable if it has non-UProperty fields | ||||
|                 IsBlittableStruct = false; | ||||
|             } | ||||
|              | ||||
|             PropertyMetaData property = new PropertyMetaData(field); | ||||
|              | ||||
|             // If we match against a backing property field use the property name instead. | ||||
|             var backingFieldMatch = backingFieldRegex.Match(field.Name); | ||||
|             if (backingFieldMatch.Success) | ||||
|             { | ||||
|                 string propertyName = backingFieldMatch.Groups[1].Value; | ||||
|                 property.Name = propertyName; | ||||
|                  | ||||
|             } | ||||
|  | ||||
|             if (property.IsInstancedReference) | ||||
|             { | ||||
|                 StructFlags |= StructFlags.HasInstancedReference; | ||||
|             } | ||||
|              | ||||
|             Fields.Add(property); | ||||
|         } | ||||
|          | ||||
|         bool isPlainOldData = true; | ||||
|         foreach (var prop in Fields) | ||||
|         { | ||||
|             if (!prop.PropertyDataType.IsBlittable) | ||||
|             { | ||||
|                 IsBlittableStruct = false; | ||||
|             } | ||||
|              | ||||
|             if (!prop.PropertyDataType.IsPlainOldData) | ||||
|             { | ||||
|                 isPlainOldData = false; | ||||
|             } | ||||
|         } | ||||
|          | ||||
|         StructFlags |= (StructFlags) GetFlags(structDefinition, "StructFlagsMapAttribute"); | ||||
|  | ||||
|         if (isPlainOldData) | ||||
|         { | ||||
|             StructFlags |= StructFlags.IsPlainOldData; | ||||
|             StructFlags |= StructFlags.NoDestructor; | ||||
|             StructFlags |= StructFlags.ZeroConstructor; | ||||
|         } | ||||
|  | ||||
|         if (!IsBlittableStruct) | ||||
|         { | ||||
|             return; | ||||
|         } | ||||
|          | ||||
|         CustomAttribute structFlagsAttribute = new CustomAttribute(WeaverImporter.Instance.BlittableTypeConstructor); | ||||
|         structDefinition.CustomAttributes.Add(structFlagsAttribute); | ||||
|          | ||||
|         TryAddMetaData("BlueprintType", true); | ||||
|     } | ||||
|  | ||||
|     [GeneratedRegex("<([a-zA-Z$_][a-zA-Z0-9$_]*)>k__BackingField")] | ||||
|     private static partial Regex BackingFieldRegex(); | ||||
| } | ||||
| @ -0,0 +1,20 @@ | ||||
| using Mono.Cecil; | ||||
|  | ||||
| namespace UnrealSharpWeaver.MetaData; | ||||
|  | ||||
| public class TypeReferenceMetadata : BaseMetaData | ||||
| { | ||||
|     public string AssemblyName { get; set; } | ||||
|     public string Namespace { get; set; } | ||||
|      | ||||
|     // Non-serialized for JSON | ||||
|     public readonly TypeReference TypeRef; | ||||
|     // End non-serialized | ||||
|      | ||||
|     public TypeReferenceMetadata(TypeReference member, string attributeName = "") : base(member, attributeName) | ||||
|     { | ||||
|         AssemblyName = member.Module.Assembly.Name.Name; | ||||
|         Namespace = member.Namespace; | ||||
|         TypeRef = member; | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,6 @@ | ||||
| namespace UnrealSharpWeaver.MetaData; | ||||
|  | ||||
| public class UnrealSharpMetadata | ||||
| { | ||||
|     public ICollection<string> AssemblyLoadingOrder { get; set; } = []; | ||||
| } | ||||
		Reference in New Issue
	
	Block a user