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();
 | 
						|
        }
 | 
						|
    }
 | 
						|
} |