200 lines
		
	
	
		
			6.6 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			200 lines
		
	
	
		
			6.6 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| 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();
 | |
|         }
 | |
|     }
 | |
| } |